From 8d5a97cca5dc3d41681e7a2dd709ac0ea93e73c5 Mon Sep 17 00:00:00 2001 From: Ian Beckwith Date: Tue, 11 Mar 2008 16:18:40 +0000 Subject: [PATCH 1/1] Imported Upstream version 211 --- COPYING.TXT | 106 + ck_crp.c | 5590 ++++++++++++++++++ ck_des.c | 98 + ck_ssl.c | 4242 +++++++++++++ ck_ssl.h | 147 + ckaaaa.txt | 385 ++ ckc211.txt | 3595 ++++++++++++ ckcasc.h | 69 + ckcbwr.txt | 1467 +++++ ckccfg.txt | 1766 ++++++ ckcdeb.h | 6369 ++++++++++++++++++++ ckcfn2.c | 3297 +++++++++++ ckcfn3.c | 2559 ++++++++ ckcfns.c | 7108 ++++++++++++++++++++++ ckcftp.c | 17126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ckcker.h | 1418 +++++ ckclib.c | 2883 +++++++++ ckclib.h | 100 + ckcmai.c | 3592 ++++++++++++ ckcmdb.c | 387 ++ ckcnet.c | 14205 ++++++++++++++++++++++++++++++++++++++++++++ ckcnet.h | 1441 +++++ ckcplm.txt | 3076 ++++++++++ ckcpro.w | 3591 +++++++++++ ckcsig.h | 214 + ckcssl.h | 126 + ckcsym.h | 5 + ckctel.c | 8946 ++++++++++++++++++++++++++++ ckctel.h | 1259 ++++ ckcuni.c | 16173 ++++++++++++++++++++++++++++++++++++++++++++++++++ ckcuni.h | 268 + ckcxla.h | 334 ++ ckermit.ini | 618 ++ ckermit70.txt | 17956 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ckermit80.txt | 10349 ++++++++++++++++++++++++++++++++ ckermod.ini | 144 + ckuat2.h | 363 ++ ckuath.c | 13287 +++++++++++++++++++++++++++++++++++++++++ ckuath.h | 228 + ckubwr.txt | 5330 +++++++++++++++++ ckucmd.c | 7639 ++++++++++++++++++++++++ ckucmd.h | 302 + ckucns.c | 2679 +++++++++ ckucon.c | 2679 +++++++++ ckudia.c | 8249 ++++++++++++++++++++++++++ ckufio.c | 8310 ++++++++++++++++++++++++++ ckuins.txt | 3519 +++++++++++ ckuker.nr | 1827 ++++++ ckupty.c | 1722 ++++++ ckupty.h | 174 + ckuscr.c | 688 +++ ckusig.c | 352 ++ ckusig.h | 79 + ckutio.c | 14492 +++++++++++++++++++++++++++++++++++++++++++++ ckututor.txt | 1959 +++++++ ckuus2.c | 13838 +++++++++++++++++++++++++++++++++++++++++++ ckuus3.c | 13310 +++++++++++++++++++++++++++++++++++++++++ ckuus4.c | 14091 ++++++++++++++++++++++++++++++++++++++++++++ ckuus5.c | 11913 +++++++++++++++++++++++++++++++++++++ ckuus6.c | 10878 ++++++++++++++++++++++++++++++++++ ckuus7.c | 14933 ++++++++++++++++++++++++++++++++++++++++++++++ ckuusr.c | 13121 +++++++++++++++++++++++++++++++++++++++++ ckuusr.h | 2974 ++++++++++ ckuusx.c | 9387 +++++++++++++++++++++++++++++ ckuusy.c | 4624 +++++++++++++++ ckuver.h | 1229 ++++ ckuxla.c | 7446 +++++++++++++++++++++++ ckuxla.h | 118 + ckwart.c | 746 +++ makefile | 7838 +++++++++++++++++++++++++ 70 files changed, 341333 insertions(+) create mode 100644 COPYING.TXT create mode 100644 ck_crp.c create mode 100644 ck_des.c create mode 100644 ck_ssl.c create mode 100644 ck_ssl.h create mode 100644 ckaaaa.txt create mode 100644 ckc211.txt create mode 100644 ckcasc.h create mode 100644 ckcbwr.txt create mode 100644 ckccfg.txt create mode 100644 ckcdeb.h create mode 100644 ckcfn2.c create mode 100644 ckcfn3.c create mode 100644 ckcfns.c create mode 100644 ckcftp.c create mode 100644 ckcker.h create mode 100644 ckclib.c create mode 100644 ckclib.h create mode 100644 ckcmai.c create mode 100644 ckcmdb.c create mode 100644 ckcnet.c create mode 100644 ckcnet.h create mode 100644 ckcplm.txt create mode 100644 ckcpro.w create mode 100644 ckcsig.h create mode 100644 ckcssl.h create mode 100644 ckcsym.h create mode 100644 ckctel.c create mode 100644 ckctel.h create mode 100644 ckcuni.c create mode 100644 ckcuni.h create mode 100644 ckcxla.h create mode 100644 ckermit.ini create mode 100644 ckermit70.txt create mode 100644 ckermit80.txt create mode 100644 ckermod.ini create mode 100644 ckuat2.h create mode 100644 ckuath.c create mode 100644 ckuath.h create mode 100644 ckubwr.txt create mode 100644 ckucmd.c create mode 100644 ckucmd.h create mode 100644 ckucns.c create mode 100644 ckucon.c create mode 100644 ckudia.c create mode 100644 ckufio.c create mode 100644 ckuins.txt create mode 100644 ckuker.nr create mode 100644 ckupty.c create mode 100644 ckupty.h create mode 100644 ckuscr.c create mode 100644 ckusig.c create mode 100644 ckusig.h create mode 100644 ckutio.c create mode 100644 ckututor.txt create mode 100644 ckuus2.c create mode 100644 ckuus3.c create mode 100644 ckuus4.c create mode 100644 ckuus5.c create mode 100644 ckuus6.c create mode 100644 ckuus7.c create mode 100644 ckuusr.c create mode 100644 ckuusr.h create mode 100644 ckuusx.c create mode 100644 ckuusy.c create mode 100644 ckuver.h create mode 100644 ckuxla.c create mode 100644 ckuxla.h create mode 100644 ckwart.c create mode 100644 makefile diff --git a/COPYING.TXT b/COPYING.TXT new file mode 100644 index 0000000..da5d39e --- /dev/null +++ b/COPYING.TXT @@ -0,0 +1,106 @@ +THE C-KERMIT 7.0 AND 8.0 LICENSE + + Last update: Thu May 1 13:52:15 2003 + +This is the new C-Kermit 7.0 and 8.0 license. The intention is to allow +C-Kermit to be distributed with "free" operating systems such as GNU/Linux, +FreeBSD, NetBSD, OpenBSD, The Hurd, etc, even when the distributions +themselves (such as Red Hat or Caldera) might be sold and/or might include +applications that are not free, and yet still require a license to include +C-Kermit in or with "non-free" products such as commercial OS's, commercial +software packages, embedded systems, and hardware (other than general-purpose +computers preloaded with "free" operating systems), since these licenses +furnish a large portion of the Kermit Project's funding. + +There have been some questions about the provision in Clause (A) that: + + The + C-Kermit source code may not be changed without the consent of the + Kermit Project, which will not be unreasonably withheld (this is + simply a matter of keeping a consistent and supportable code base). + +The intention of this clause is primarily to make sure that anybody who +makes modifications sends them back to us, since we are the ones have to +support C-Kermit, and so we can carry them through to future releases (so +you don't have to make the same changes again and again). + +Secondarily it is to protect Columbia University in the unlikely event of +modifications made with deliberate intent to offend or cause damage. + +Any redistributor of C-Kermit under Clause (A) below should rest assured +there is no intention of preventing them from constructing a distribution in +the appropriate format (RPM or whatever) for their product or from issuing +any patches required for their products; we simply want to be informed so we +can maintain a consistent code base and a solid, supportable software +package. We are happy to work with any redistributor an any issues that +concern them. If you have questions, send them to kermit@columbia.edu. + +Note: All changes to this file since 1 January 2000 (the C-Kermit 7.0 +release date) are above; the license itself has not changed, except to +update the most recent copyright date. + +(Begin) + +Copyright (C) 1985, 2003, + The Trustees of Columbia University in the City of New York. + All rights reserved. + +PERMISSIONS: + +The C-Kermit software may be obtained directly from the Kermit Project at +Columbia University (or from any source explicitly licensed by the Kermit +Project or implicitly licensed by Clause (A) below) by any individual for +his or her OWN USE, and by any company or other organization for its own +INTERNAL DISTRIBUTION and use, including installation on servers that are +accessed by customers or clients, WITHOUT EXPLICIT LICENSE. + +Conditions for REDISTRIBUTION are as follows: + +(A) The C-Kermit software, in source and/or binary form, may be + included WITHOUT EXPLICIT LICENSE in distributions of OPERATING + SYSTEMS that have OSI (Open Source Initiative, www.opensource.org) + approved licenses, even if non-Open-Source applications (but not + operating systems) are included in the same distribution. Such + distributions include, but are not limited to, CD-ROM, FTP site, + Web site, or preinstalled software on a new GENERAL-PURPOSE + computer, as long as the primary character of the distribution is + an Open Source operating system with accompanying utilities. The + C-Kermit source code may not be changed without the consent of the + Kermit Project, which will not be unreasonably withheld (this is + simply a matter of keeping a consistent and supportable code base). + +(B) Inclusion of C-Kermit software in whole or in part, in any form, in + or with any product not covered by Clause (A), or its distribution + by any commercial enterprise to its actual or potential customers + or clients except as in Clause (A), requires a license from the + Kermit Project, Columbia University; contact kermit@columbia.edu. + +The name of Columbia University may not be used to endorse or promote +products derived from or including the C-Kermit software without specific +prior written permission. + +DISCLAIMER: + + THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS + FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF + COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT + BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, + OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR + IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS + HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL + INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN + THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY + AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING + ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE. + +The above copyright notice, permissions notice, and disclaimer may not be +removed, altered, or obscured and shall be included in all copies of the +C-Kermit software. The Trustees of Columbia University in the City of +New York reserve the right to revoke this permission if any of the terms +of use set forth above are breached. + +(End) diff --git a/ck_crp.c b/ck_crp.c new file mode 100644 index 0000000..c35afd0 --- /dev/null +++ b/ck_crp.c @@ -0,0 +1,5590 @@ +char *ckcrpv = "Encryption Engine, 8.0.114, 9 Oct 2003"; +/* + C K _ C R P . C - Cryptography for C-Kermit" + + Copyright (C) 1998, 2004, + 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 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 *,long)=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, long 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 +#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; + rc = des_random_key(B); + return(rc); +} + +#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. + */ +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 + hexdump("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) { + type = *typelist++; + 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; + } + } + } + if (use_type) { + ep = findencryption(use_type); + if (!ep) { + debug(F111,"encrypt_support","findencryption == NULL",use_type); + return(-1); + } + type = ep->start ? (*ep->start)(DIR_ENCRYPT, 0) : 0; +#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(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(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(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(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(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(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(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)); + hexdump("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); + } + + hexdump("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)); + + hexdump("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); + } + hexdump("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)); + + hexdump("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); + } + hexdump("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(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(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(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(buf,"Can't set 3DES session key (%d < %d)", + key ? key->length : 0, 2 * 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; + } + hexdump("des3_session_key key->data",key->data,sizeof(Block)); + hexdump("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); + } + hexdump("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)); + + hexdump("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); + } + hexdump("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)); + + hexdump("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); + } + hexdump("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(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 */ diff --git a/ck_des.c b/ck_des.c new file mode 100644 index 0000000..c534652 --- /dev/null +++ b/ck_des.c @@ -0,0 +1,98 @@ +/* + 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 */ diff --git a/ck_ssl.c b/ck_ssl.c new file mode 100644 index 0000000..7b53770 --- /dev/null +++ b/ck_ssl.c @@ -0,0 +1,4242 @@ +char *cksslv = "SSL/TLS support, 8.0.221, 26 Feb 2004"; +/* + C K _ S S L . C -- OpenSSL Interface for C-Kermit + + Copyright (C) 1985, 2004, + 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. + 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 */ + +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_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_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)); + +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); + + 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 (ssl_verbose_flag && !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; + + 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 (ssl_verbose_flag && 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; + + 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 */ +{ + 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; + extern int quiet; + +#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; + extern int quiet; + +#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 ( !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 ( !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; + extern int quiet; + + if ( !ck_ssleay_is_installed() ) + return(0); + + debug(F111,"tls_load_certs","SSL_CTX",ctx); + debug(F111,"tls_load_certs","SSL",con); + debug(F111,"tls_load_certs","server",server); + + 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; + + if ( !ck_ssleay_is_installed() ) + return; + + 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."); + printf(" SSL\\TLS support 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; + 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 err; + X509 *server_cert; + + if (server_cert = SSL_get_peer_certificate(ssl)) { + err = X509_NAME_get_text_by_NID(X509_get_subject_name(server_cert), + NID_commonName, name, sizeof(name)); + X509_free(server_cert); + } + 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; + 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 = 0, c2 = 0, 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; + 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 (ssl_verbose_flag) + printf(prefix); + return(ok); +} + +#ifndef HPUX10 +#ifndef HPUX1100 +#ifndef SCO_OSR505 +#ifndef OpenBSD +#ifndef FREEBSD4 +#ifndef LINUX +#ifndef AIX41 +#ifndef UW7 +#ifndef SOLARIS9 +#ifndef SOLARIS8 +#ifndef SOLARIS7 +#ifdef DEC_TCPIP +#define inet_aton INET_ATON +#endif /* DEC_TCPIP */ +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 /* SOLARIS7 */ +#endif /* SOLARIS8 */ +#endif /* SOLARIS9 */ +#endif /* UW7 */ +#endif /* AIX41 */ +#endif /* LINUX */ +#endif /* FREEBSD4 */ +#endif /* OpenBSD */ +#endif /* SCO_OSR505 */ +#endif /* HPUX1100 */ +#endif /* HPUX10 */ + +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; + + if (ssl_verbose_flag && !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; + + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) { + printf("[SSL - OK]\r\n"); + } + debug(F110,"ssl_reply","[SSL - OK]",0); + + ssl_active_flag = 1; + ssl_display_connect_details(ssl_con,0,ssl_verbose_flag); + } + } + 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 ( ssl_verbose_flag ) { + 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; + + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + printf("[SSL - OK]\r\n"); + } + debug(F110,"ssl_is","[SSL - OK]",0); + ssl_active_flag = 1; + ssl_display_connect_details(ssl_con,1,ssl_verbose_flag); + + /* 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); + + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + 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,ssl_verbose_flag); + + +#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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) { + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) + 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 ( ssl_verbose_flag ) + 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 ( 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 */ diff --git a/ck_ssl.h b/ck_ssl.h new file mode 100644 index 0000000..b4dd8a3 --- /dev/null +++ b/ck_ssl.h @@ -0,0 +1,147 @@ +/* + C K _ S S L . H -- OpenSSL Interface Header for C-Kermit + + Copyright (C) 1985, 2004, + 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 +#include +#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; + +#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 *)); +#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 */ diff --git a/ckaaaa.txt b/ckaaaa.txt new file mode 100644 index 0000000..efa83a5 --- /dev/null +++ b/ckaaaa.txt @@ -0,0 +1,385 @@ +ckaaaa.txt 10 Apr 2004 + + C-KERMIT VERSION 8.0.211 + 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://www.columbia.edu/kermit/ - kermit@columbia.edu + + + Copyright (C) 1985, 2004, + 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. + + +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, + supplementated by Web-based updates for C-Kermit 7.0 and 8.0. + +PLATFORMS + Security + Name Included Last Updated + + Unix Yes 8.0.211 10 Apr 2004 + (Open)VMS No 8.0.208 10 Apr 2004 + 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, and build with CKVKER.COM. + +Others: In the kermit/f or kermit/test directories under the appropriate +prefixes, explained below. + + +INSTALLATION + +Installation procedures depend on the system. Please read the CK?INS.TXT, +if any, file for your system (?=U for UNIX, V for VMS, etc). Please note +the naming and placement for the initialization files: + + CKERMIT.INI + The standard initialization file. Please leave it as is unless you + know what you are doing and (if you are changing it or replacing it + for others to use) you are prepared to support it. Rename this file + to .kermrc in UNIX, OS-9, BeBox, or Plan 9. In Stratus VOS, rename + it ckermit.ini (lowercase). On multiuser systems, it goes either in the + (or EACH) user's home (login) directory, or else in a common shared + place if C-Kermit has been configured to look in that place (see + ckccfg.txt for details). + + CKERMOD.INI + A *sample* customization file. On multiuser OS's, a copy of this file + goes in each user's home directory, and then each user edits it to suit + her needs and preferences; e.g. by defining macros for their common + connections. + + 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) + 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): + + ckcker.ann - Release announcements. + ckccfg.txt - Configuration information (feature selection), general. + ckcplm.txt - Program logic manual (for programmers). + 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 conventiently +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 cku211.tar.Z (get the tar archive) (or get cku192.tar.gz) + bye (disconnect and exit from ftp) + + uncompress cku211.tar.Z (at the shell prompt, uncompress the archive) + tar xvf cku211.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] + Mac: ck[cuwm]*.[cwhr] + AOS/VS: ck[cuwd]*.[cwh] + VOS: ck[cwhl]*.[cwh] + Amiga: ck[cuwi]*.[cwh] + Atari: ck[cuws]*.[cwh] + OS-9: ck[cuw9]*.[cwha] + +For a detailed, specific source file list for this C-Kermit release, see the +file ckcxxx.txt, where xxx is the current C-Kermit edit number, such as 211. + +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 + 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. cku211.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-depdendent 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) diff --git a/ckc211.txt b/ckc211.txt new file mode 100644 index 0000000..1886725 --- /dev/null +++ b/ckc211.txt @@ -0,0 +1,3595 @@ +C-KERMIT CHANGE LOG (Changes since 8.0.200 of 12 Dec 2001) + +Chronological order: Go to the bottom to find the newest edits. + +---8.0.200--- + +Known bugs (+ = fixed after release): + + + 1. tilde_expand() can call getcwd() with NULL arg. + + 2. getexedir() called too early (fatal in combination with (1)). + + 3. Kermit "get blah" where blah is a symlink; server refuses to send it. + Should not do this if GET not recursive. + ? 4. Dave Sneddon's report about VMS fore/background confusion. + + 5. FTP GET path/file doesn't work - path not stripped - but MGET works. + + 6. IRIX 5.3 compilation problems (have patches from Marcus Herbert) + X 7. Filename completion bug (see below) (deferred). + + 8. QNX6 herald and other problems. + +------------- + +Merged Jeff's changes, 20 Dec 2001: + + . Changed all occurrences of "ttnproto == NP_TELNET" to "IS_TELNET()" to + account for the difference between SSH and Telnet. ckuscr.c, + ckuus[3457].c, ckcnet.h, ckcfns.c, ckudia.c, ckutio.c, ckucon.c, ckucns.c. + + . Moved SSH pty failure warnings. ckuusr.c. + + . Security adjustments to FTP module, plus fix an error message. ckcftp.c. + + . Adjustment of some security-related #ifdefs. ckcdeb.h, ckuus2.c, ckctel.c. + + . Guard against calling getpwnam() with a NULL arg in tilde_expand() ckufio.c. + + . Moved getexedir() call to later, where it's safe. ckcmai.c. + +Added SSH ADD and many SSH SET commands from Jeff's spec. Fixed SHOW SSH +to not dump core if variables weren't set. ckcker.h, ckuus[r3].c, 20 Dec 2001. + +C-Kermit in server mode, client says "get foo" where foo is a symlink. +Server says "no files meet selection criteria" instead of sending the file. +It should only refuse to follow symlinks if it's a recursive get. Fixed +in sgetinit(): ckcpro.w, 21 Dec 2001. + +More work on SSH and SET/SHOW SSH commands. ckuus[r3].c, 21 Dec 2001. + +Undid Jeff's replacement of the SSH pseudoterminal allocation failure +message, because now it comes out any time an SSH command has to be +reparsed (in the non-SSHBUILTIN case). ckuusr.c, 21 Dec 2001. + +More SSH and SET SSH command work back & forth with Jeff, plus Jeff added +SET HOST /NET:SSH. ckcmai.c, ckuus[r37].c, ckcdeb.h, ckuusr.h, 22 Dec 2001. + +Added SSH OPEN switches. ckuusr.c, 22 Dec 2001. + +Added SSH CLEAR, HELP SSH, and HELP SET SSH. ckuus[r2].c, 23 Dec 2001. + +From Jeff: + . SET TCP commands now apply to SSH + . SSH V2 REKEY and FORWRD-{LOCAL,REMOTE}-PORT commands now implemented + . Missing DLLs automatically disable appropriate authentication mechanisms. +ckuusr.c ckcnet.c ckuus3.c ckcmai.c ckcnet.h ckuus4.c, 26 Dec 2001. + +From Jeff: + . Remove SET SSH KEEPALIVES. + . Add help text for SSH AGENT { ADD, DELETE, LIST }. +ckuus[23].c, 28 Dec 2001. + +Added parsing for SSH AGENT { ADD, DELETE, LIST }. ckuusr.c, 28 Dec 2001. + +From Jeff: + . Fixed a crash that can happen when making an SSH connection. + . Filled in SSH AGENT actions. + . Changed default for strict host key check (to ASK) and help text. + . uploaded new binaries include ~kermit/os2test/beta/ssh-agent.exe + . Read man ssh-agent on ftp.kermit.columbia.edu for details on what it does. +ckuus[r23].c, 28 Dec 2001. + +"ftp get path/filename" didn't work; the FTP client did not strip the path +from the local copy of the filename when doing a GET, even though it did +for MGET. Diagnosis: in doftpget(), the "if (!getone && !skipthis)" statement +lacked an "else" part for the getone case. ckcftp.c, 28 Dec 2001. + +A while back Jeff reported that in FTP MGET, if you cancel a file with 'x', +all the rest of the files arrive truncated to 0 bytes. I tried this on both +Unix and Windows and couldn't reproduce it. + +In the last-minute flurry to release C-Kermit 8.0, I thought I noticed the FTP +client failing to update the fullscreen file-transfer display. But it seems +to work right, at least in Unix. When downloading a big file with FTP, all +the display fields are updated as expected. But smaller files might go by too +fast for the display to do anything. HOWEVER, in K95 the file transfer +display does not update itself until the end of the file, even if the file +takes a long time to transfer. This happens in both the Console and GUI +versions. A thread thing? (Jeff says no.) Yet the same display works fine +on Telnet connections. + +In IRIX 5.3, the select()-based CONNECT module had to include +or else it blew up with "struct timeval" unknown. Since there already was +a SYSTIMEH CFLAG, I added the #include within #ifdef SYSTIMEH..#endif and +rebuilt with KFLAGS=-DSYSTIMEH, only to discover that the irix5* targets +didn't bother to propogate KFLAGS. Fixed in ckucns.c, makefile, 30 Dec 2001. + +Increased IRIX5x Olimit from 2400 to 3000 because of ckuus[34].c. Added +-ansi, since (Marcus Herbert reported) we were not actually getting ANSI-C +compilation even though CK_ANSIC was defined. But now that we are, we get +warnings in , which is included by ckcnet.h: + + bit-field 'th_off' type required to be int, unsigned int, or signed int. + (3.5.2.1(30)) + u_char th_off:4, + ------ ^ +Tough. makefile, 30 Dec 2001. + +But adding -ansi to the IRIX 5x targets also make compilation bomb whenever we +referenced fdopen() or popen(), which evidently don't have prototypes in any +of the header files. Luckily we already have CFLAGS for this occasion too: +DCLFDOPEN and DCLPOPEN. Added these to the irix51 target. Also had to copy +the fdopen()-popen() prototype section to ckuusx.c, which has a new reference +to fdopen() in a workaround for the curses console buffering bug. makefile, +ckuusx.c, 30 Dec 2001. + +The QNX6 version did not receive a proper herald (it announced itself as +"unknown version". Reshuffled #ifdefs in ckuver.h, added display of QNX6 +and NEUTRINO symbols to ckuus5.c, 30 Dec 2001. + +Lucas Hart sent in a patch for the VMS problem. Apparently it was even worse +than Dave Sneddon had reported: 8.0 couldn't run at all under Batch. ckvtio.c, +31 Dec 2001. + +A major obstacle to the usability of the FTP client is that certain commands +don't behave as FTP users expect: CD, DIR, DELETE, MKDIR, etc, which are local +rather remote, and there are no LCD (etc), USER, or ACCOUNT commands. We +could fix this by adding an FTP command-language personality, but file +management commands can also be remote or local on connections to Kermit +servers too. So: + +SET LOCUS { LOCAL, REMOTE, AUTO } + Sets the locus for unprefixed file management commands. + When LOCAL, a REMOTE (or R) prefix is required for + to send file management commands to a remote server (e.g. RCD, RDIR). + When 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 used for declaring local variables. + +This 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 connnection is closed. + +Implementation (31 Dec 2001): + . None of this is compiled if LOCUS is not defined. + . Added XYLOCUS (SET LOCUS) and LOCUS definitions: ckuusr.h. + . Override by defining NOLOCUS (which inhibits definition of LOCUS). + . Added LOCUS to SET keyword table: ckuusr.c. + . Added locus & autolocus variables: ckuusr.c. + . Added SET LOCUS parsing and variable setting: ckuus3.c. + . Added display of LOCUS setting to SHOW COMMAND: ckuus5.c. + . Added automatic locus setting to setlin(): ckuus7.c. + . Added automatic locus setting to ftpopen() and ftpclose(): ckcftp.c. + +How to catch all the places where a Kermit connection is closed? Turns out +we've done this before, when we added the connection log. So I made +dologend() take care of locus switching. But dologend() was not compiled in +if certain symbols were defined, such as NOLOCAL, or not defined, such as +CKLOGDIAL. So I (a) rearranged the #ifdefs so that even if these would +otherwise have obliviated dologend(), now they leave a piece of it for +locus-setting; (b) moved the prototype out of #ifdefs; and (c) took all calls +to it out of #ifdefs. ckcker.h, ckcfn2.c, ckcmai.c, ckucns.c, ckucon.c, +ckuus[r347x].c, 31 Dec 2001. + +Added locus checking to the following commands: DIRECTORY, CD/CWD, CDUP, +DELETE, PWD, MKDIR, RMDIR, RENAME. ckuusr.c, 31 Dec 2001. + +Added LDIRECTORY, LCD/LCWD, LCDUP, LDELETE, LPWD, LMKDIR, LRMDIR, +LRENAME. ckuusr.[ch], 31 Dec 2001. + +Added USER and ACCOUNT commands, which are the same as FTP USER and FTP +ACCOUNT. ckuusr.[ch], ckcftp.c, 31 Dec 2001. + +Since automatic locus switching could be a big surprise for most people, I +printed message any time it changed. ckcftp.c, ckuus[37].c, 31 Dec 2001. + +Added help text for the new L commands and filled in missing HELP text for +SET GET-PUT-REMOTE, CDUP, MKDIR, and RMDIR. ckuus2.c, 31 Dec 2001. + +Changed help text of CD, DIR, etc, for LOCUS. Changed the help text for +RCD, RPWD, RDEL, RDIR, etc, to mention that they also work with FTP servers. +Updated HELP REMOTE for this too. ckuus2.c, 31 Dec 2001. + +Made sure code builds with NOLOCAL, NOLOGDIAL, and NOLOCUS (it does). + +The IKSD command, when given with a /USER: switch, sends the user ID to the +IKSD. But the SET HOST /USER: command does not, when making a connection to a +Kermit service. This makes it impossible to script IKSD interactions using +only client commands. Furthermore, even if you include a /PASSWORD switch +with the IKSD command, it does not send the password. I added code near the +bottom of setlin() to do this. If we have a connection to a Kermit service +and a /USER: switch was given, then we attempt a REMOTE LOGIN. If a +/PASSWORD: switch was not given then if the username is "ftp" or "anonymous", +we automatically supply a password of user@host; otherwise we prompt for a +password. If a /USER: switch was not given, it acts like before. It all +works, but it might not be the best way (or place) to do it. setlin(): +ckuus7.c, 31 Dec 2001. + + NOTE: The above change doesn't help with IKSD /USER:anonymous, + the server prompts for password anyway, not sure why. + + NOTE 2: What about secure authentication? We have to test to see + if user was already authenticated before sending the login packet. + +Added /opt/kermit and /opt/kermit/doc to info_dir[] list (for Solaris). +ckuus5.c, 31 Dec 2001. + +From Jeff: new Help text for SET TERM FONT (K95 GUI). ckuus2.c, 1 Jan 2002. + +More work on help text for file management commands -- e.g. we can't lump +the L-commands together with the unprefixed ones; they need separate entries. +Also: added missing HELP REMOTE PWD, improved the default case (in which +help text had been omitted for a valid command). ckuus2.c, 1 Jan 2002. + +It seems VMS C-Kermit was pretty much ignoring the -B (force background) and +-z (force foreground) command-line options. Fixed in congm(): ckvtio.c, +1 Jan 2002. + +Tested the SET LOCUS business with VMS C-Kermit, which does not have a +built-in FTP client. Of course in this case there is no automatic locus +switching, but SET LOCUS REMOTE works nicely on IKSD connections. + +From Jeff: + . #ifdef adjustments for LOCUS changes. + . SSH KEY CREATE /TYPE:SRP. + . Fix \v(serial) to not be 8N2 by default if speed is 0. + . Don't let doexit() run if sysinit() hasn't been called first. +ckuus[r247x].c, 2 Jan 2002. + +Made SET BACKGROUND { ON, OFF } do exactly the same as -B and -z options. +ckuus3.c, 2 Jan 2002. + +Updated user-visible copyright dates to 2002 (but still need to do all the +source-module comments). ckcmai.c, ckuus[25].c, 2 Jan 2002. + +Rearranged #include in ckucns.c that was done for IRIX 5.3, +to avoid conflicts in SV/68 R3v6. 3 Jan 2002. + +From Dave Sneddon: Code changes in VMS sysinit() and congm() to work around +problems in batch, SPAWN'd, etc, and change CTTNAM from TT: to SYS$INPUT:. +ckcdeb.h, ckvtio.c, 3 Jan 2002. + +From Jeff: + . Fixed typo in definition of CTTNAM for VMS. ckcdeb.h + . Moved macro definitions for SSHBUILTIN from ckuus3.c to ckuusr.h + so they can be referenced in ckuus7.c + . Added SSH functionality to SET HOST: + SET HOST /NET:SSH /CONNECT hostname [port] /switches + . Fixed SET NET TYPE so it won't reject SSH if SSH is installed. + . Changes to allow IKSD to continue functioning. Somehow this minor change + to ckcmai.c got lost in one of the back and forth exchanges. + . HELP TEXT for UCS2 kverb + . Fix a problem in K95 where multiple threads could be attempting to + send a telnet negotiation simultaneously. +ckcmai.c ckcdeb.h ckuus2.c ckuus3.c ckuusr.c ckuusr.h ckuus7.c ckctel.c +ck_crp.c ckuat2.h ckuath.c, 4 Jan 2002. + +From Jeff: + + Peter Runestig complaining that the Telnet Forward X code was corrupting + data. This resulted in a very thorough examination of the telnet module + code and a discovery of some rather significant problems. The root of the + problems is the lack of thread safety. To correct this problem the + following was done. + + All code (regardless of module) which outputs telnet commands is placed + into a mutex region to ensure that competing output threads do not result + in interleaving their output. This could happen for instance when the + forward-x thread is forwarding data and the user changes the window size + or sends an AYT or BREAK. Next the buffer used for input and output + processing were identical. This means that output data could be treated + as input or vice versa. Ugh.... + + I also spent some more time cleaning up setlin(). Mostly reorganizing the + code into single if (...) blocks so that breaking it up will be easier. + +ckctel.c ckuus7.c, 4 Jan 2002. + +Updated internal copyright notices. All modules, 5 Jan 2002. + +From Jeff: + More of same, plus new makefile target and changes from Spike Gronim + for freebsd44+srp+openssl. +ckcdeb.h ckcnet.c ckctel.c ckuus7.c ck_ssl.c makefile, 5 Jan 2002. + +Some minor updates and fixes to SSH and SET SSH help text. +ckuus2.c, 6 Jan 2002. + +Added SET RGB-COLORS for GUI. ckuusr.[ch], ckuus3.c, 6 Jan 2002. + +From Jeff: More Telnet changes, Debug semaphores for K95, etc: ckcdeb.h, +ckuusr.h, ckuus[r35x].c, ckctel.[ch], ckuath.c, 7 Jan 2002. + +Added --xpos:n --ypos:n, SET GUI WINDOW POSITION x y, and changed SET +RGB-COLORS to SET GUI RGBCOLOR. Action needs to be filled in (in setguiwin() +in ckuus3.c), and gui_xpos and gui_ypos need to be defined in cko???.c. +ckuusr.h, ckuus[r3y].c, 7 Jan 2002. + +Added --fontname:name --fontsize:name (and facename as synonym for fontname). +ckuusr.h, ckuus[7y].c, 7 Jan 2002. + +Moved GUI (not OS/2) SET TERM FONT code in ckuus7.c to its own routine, +setguifont(), in ckuus3.c, and made GUI SET TERM FONT call this routine, +and also made SET GUI FONT call the same routine. ckuus[37].c, 7 Jan 2002. + +Added --termtype:, --height:, --width:, --user:. Also added symbols for +--telnet:, --ssh:, --ftp:, --[remote-]charset, and --password:, but didn't +fill them in. --password: is probably not a good idea (but we allow it for +FTP); the others involve a lot of code-shuffling and reconciliation, which +I'll try to do when I get a chance (especially the connection ones, which +can be done as part of the setlin() restructuring). ckuusr.h, ckuusy.c, +8 Jan 2002. + +Also I tried commenting out the #ifndef KUI..#endif's around SET TERMINAL +CHARACTER-SET (easier said than done because a crucial #endif was mislabeled). +Let's see if it compiles & works... ckuus7.c, 8 Jan 2002 + +Added FTP [ OPEN ] /NOINIT, meaning don't send REST, STRU, and MODE commands +upon making an FTP connection. This allows connection to servers that close +the connection (or worse) when given these commands (e.g. Linux 2.4 TUX 2.0 +FTP server). ckcftp.c, 8 Jan 2002. + +Looked at adding caller ID support for the ANSWER command: + + . SET ANSWER CALLER-ID { ON, OFF } + . SET ANSWER RINGS + . \v(callid_xxx) xxx = { date, time, name, nmbr, mesg } + . CKD_CID modem capability + . Set CKD_CID for modems that have it. + . A quick survey shows: + - USR V.90: No (but Jeff says some USRs have it). + - V.250: No + - Lucent Venus: No + - USR: #CID=1 (the ones that have it -- X2?) + - Diamond Supra: #CID=1 + - Rockwell 56K: #CID=1 + - PCTEL: #CID=1 + - Zoltrix: +VCID=1 + - Conexant: +VCID=1 + . Since there are different commands to enable caller ID reporting, + we need a new field in struct MDMINF. + . SHOW MODEM and SHOW DIAL would need updating. + . etc etc... + +This is all way too much for now so I just did the setting of the \v(callid_*) +variables. These are reset at the beginning of an ANSWER command, and then +set by the ANSWER command if they come in; thus they persist from the time +they are collected until another ANSWER command is given. To take advantage +of autoanswer, the user has to enable it in the modem (all the modems I found +that support it have it disabled by default), and also has to set the number +of rings to at least 2. This can be done with (depending on the modem): + + set modem command autoanswer on ATS0=2#CID=1\{13} + set modem command autoanswer on ATS0=2+VCID=1\{13} + +and undone with: + + set modem command autoanswer on ATS0=1#CID=0\{13} + set modem command autoanswer on ATS0=1+VCID=0\{13} + +The variables can be accessed only after the call is answered. Therefore the +only way to refuse a call is to answer it, inspect the variables, and then +hang it up if desired. Future Kermit releases can do this more nicely (as +sketched out above.) Also while I was in the dialing code, I added result +code VCON (= VOICE), used by several of the newer modems. These changes are +untested. The SET ANSWER command is written but commented out. ckuusr.h, +ckcker.h, ckuus[r3].c, ckudia.c, 8 Jan 2002. + +From Jeff: fixes to --termtype:, --height:, --width:, --user:, and filling in +of --rcharset:, which required extracting code from settrm() into a separate +parse-method-independent remote character-set setting routine. ckuus[7y].c, +8 Jan 2002. + +From Jeff: More work on TERMINAL CHARACTER-SET code reorganization, and +reinstatement of SET TERMINAL CHARACTER-SET in K95G. Also, fix char/CHAR +warnings in Telnet module. ckuus7.c, ckctel.c, 9 Jan 2002. + +Made SET TERM CHARACTER-SET visible for all builds, including K95G, and filled +in HELP text for it. ckuus[27].c, 9 Jan 2002. + +Added help text for new extended options. ckuusy.c, 9 Jan 2002. + +Commented out the return(-2) statement at the end of xgnbyte() to make the +"Statement not reached" errors go away, after checking to make sure that there +was no path that could fall through to the end. I'm 99.99% sure there isn't, +but that doesn't mean that some compilers might not still complain. ckcfns.c, +9 Jan 2002. + +From Jeff: fix typo in the K95 extended-option help text; add more +semaphores to network i/o. ckuusy.c, ckcnet.c, 10 Jan 2002. + +Undid ansiisms in set{lcl,rem}charset() declarations. ckuus7.c, 10 Jan 2002. + +Removed a duplicated clause from the install target. makefile, 10 Jan 2002. + +From Jeff: more semaphores. ckcnet.c, 11 Jan 2002. + +Moved references to tmpusrid and tmpstring out of NOSPL #ifdefs -- they can +be used with NOSPL. setlin(): ckuus7.c, 13 Jan 2002. + +Made a dummy dologend() routine outside of #ifndef NOICP, so we don't have +to enclose every reference to dologend in #ifdefs. (I had added a bunch of +calls to dologend() throughout the code to handle automatic LOCUS switching.) +ckuus3.c, 13 Jan 2002. + +Moved "extern int nettype" outside of NOICP #ifdefs in ckuus4.c for NOICP +builds. 13 Jan 2002. + +Moved a misplaced #ifdef in the VERSION command. ckuusr.c, 13 Jan 2002. + +Did 81 different feature-selection builds on Linux (RH 7.0), all OK after the +changes listed above for today. 13 Jan 2002. + +Added prototypes for set{rem,lcl}charset(). ckcxla.h, 13 Jan 2002. + +Added ckcxla.h to dependencies for ckuusy.c. ckvker.com, 13 Jan 2002. + +Made a correction to the HELP SET LOCUS text and supplied a missing comma +for HELP REMOTE. ckuus2.c, 13 Jan 2002. + +Built OK on HP-UX 11.11 (K&R and ANSI), Solaris 8 (cc), Solaris 2.5.1 (gcc), +SunOS 4.1.3 (cc and gcc), VMS 7.1 (DEC C, net and nonet), Unixware 7.1.1, +Tru64 4.0G, HP-UX 10.20 (K&R), AIX 4.3.3, FreeBSD 2.2.8, Slackware 8.0, IRIX +6.5.13f, IRIX 5.3 (??? Can't tell -- the computer ran out of swap space -- but +it was OK a few days ago), VMS 5.5-2 (VAX C, UCX + nonet)... HP-UX 9.05, ... + +Some corrections to comments in HP targets from PeterE. makefile, 14 Jan 2002. + +Corrections to prototypes for set{rem,lcl}charset() (VOID, not void) from Jeff. +ckcxla.h, 14 Jan 2002. + +Builds, cont'd... SINIX 5.42, Red Hat Linux 5.2 on i386, SuSE 7.0 on S/390, +Red Hat 7.1 on IA64, QNX 4.25, HP-UX 5.21/WinTCP, ..., + +Dell Coleman noticed that in AIX, the COPY command always +says "Source and destination are the same file" when the destination file +doesn't exist. This is because in AIX, realpath() fails with ENOENT (errno +2). The zfnqfp() code already accounts for this, but evidently not well +enough. So I did what I should have done long ago. zfnqfp() was originally +accomplished with do-it-yourself code. Later I added support for realpath(), +and partitioned the routine into mutually exclusive compile-time sections: +#ifdef CKREALPATH realpath()... #else do-it-yourself... #endif. But if +realpath() failed, there was no recourse to the do-it-yourself code. Today I +replaced the #else with the #endif, so the do-it-yourself part is always +included and is executed if the realpath() call fails. Built and tested on +AIX 4.3.3 and Solaris 2.5.1, as well as on Linux with and without the +realpath() code included. zfnqfp(): ckufio.c, 16 Jan 2002. + +Separated K95 and C-Kermit test version numbers, so C-Kermit can be RC.02 +while K95 is Beta.01. ckcmai.c, 16 Jan 2002. + +Inhibited 0-length writes by conol() and conoll(), since they cause big +trouble with the AIX 4.3.3 pty driver, e.g. when you have an SSH connection +into AIX and run C-Kermit there. ckutio.c, 16 Jan 2002. + +Suppressed "Switching LOCUS..." messages from FTP client when it was invoked +from the command line. ckcfns.c, 17 Jan 2002. + +Dave Sneddon noticed that FOPEN /APPEND gets "?Write access denied" in VMS +if the file exists. This is apparently because VMS zchko() does the wrong +thing. Commenting out the call zchko() in the VMS case gets past this but +then the appended part of the file has different attributes than the orignal +part, e.g.: + + abc <- original line (horizontal, normal) + d <- appended line (vertical) + e + f + +VMS fopen() takes an optional 4th argument: a series of RMS keyword=value +pairs. Kermit doesn't give any. Experimentation shows that appending to +a Stream_LF works fine. That'll be a restriction for now, until somebody +sends in code to get the RMS attributes of the original file and feed them +to fopen(). Also need code to fix VMS zhcko() to say whether it's OK to +append to a file. ckuus7.c, 17 Jan 2002. + +Somebody suggested I could get a working Kermit for Neutrino 2+ by doing the +QNX6 build on Neutrino itself. I verified that this can't be done -- at least +not by me -- since Netutrino 2+ doesn't have a compiler, and we already know +the version cross-built for it on QNX4 doesn't work. 17 Jan 2002. + +From Jeff: SET SSH GSSAPI KEY-EXCHANGE { ON, OFF } parsing, SHOW SSH. +ckuus3.c, 18 Jan 2002. + +PeterE suggested that SET ESCAPE allow 8-bit escape characters because of the +difficulty in entering Ctrl-\ on European keyboards and the hardship (e.g. to +EMACS and VI users) of sacrificing another C0 control character. Like +everything these days, this turns out to be rather a bigger deal than it would +seem. The SET ESCAPE parser calls setcc(), which accepts control characters +in various formats (literal, ^X notation, or numbers), and gives an error +return if the value is not 0-31 or 127. This is changed easily enough to also +allow numbers between 128 and 255. But who else calls setcc()? The commands +for setting Kermit packet start and end characters. No big deal, this gives +people a bit more flexibility in case they need it, but it won't be +documented. setcc(): ckuus7.c, 18 Jan 2002. + +Since code to display the escape character is scattered all over the place, +and some of it indexes into an array based on the character value (which would +now dump core if the escape character was > 128), I put the code in one place, +a new shoesc() routine in ckuusx.c (which needs to be outside #ifndef NOICP, +since the CONNECT modules use it even in command-line only builds). Also +discovered that this code was indexing into the nm[] array with tt_escape to +get "enabled" or "disabled", which is no longer appropriate, so fixed this +too. ckuusr.h, ckuus[5x].c, 18 Jan 2002. + +Made SHOW ESCAPE, SHOW TERM, and the various CONNECT modules call shoesc(), +and updated HELP SET ESC. ckuus[25].c, ckucns.c, ck[cuvd9]con.c, 18 Jan 2002. + +After all that, it occurred to me that this is a really bad idea for K95, +with all the confusion about Console code pages, OEM code pages, Windows +code pages, and Unicode. But I tried "echo \161" at the K95 prompt and got +the expected 8-bit character in both the Console version and the GUI, so +maybe it's OK after all. + +Removed the automatic IKSD login code from setlin() since it complicates +interactive anonymous login. ckuus7.c, 20 Jan 2002. + +An #ifdef clause from Matthew Clarke to avoid "redeclaration of free" error +when building a curses version of C-Kermit for AIX 2.2.1 on RT PC. ckuusx.c, +22 Jan 2002. + +Took care of one detail I omitted when adding the 8-bit escape character: +not stripping the 8th bit before comparing the keyboard char with the escape +char. ck[uv]con.c, ckucns.c, 24 Jan 2002. + +Started to go through Jeff's changes of the last week but he had run trim -t +on them, which untabifies, so the diffs were huge. Retabifying Jeff's files +only makes matters worse. So instead of comparing each old and new source +file in EMACS windows with M-X Compare-Windows like I usually do (which can't +be told to ignore whitespace), I had to work from the diff -c -b listings. +In ascending order of size of diffs: + +ckcker.h: Add I_AM_SSHSUB definition. +ckuusr.h: XXLINK and VN_PERSONAL, etc, definitions. +ckuusy.c: Support for "I Am SSHSUB" invocation. +ckuus5.c: Support for new K95 directory structure. +ckcmai.c: Init endianness earlier (K95 TYPE was broken), "I Am SSHSUB" support. +ckuus7.c: Security #ifdefs, SSH OPEN /PASSWORD, SSHSUB support +ckcftp.c: <-- SAVE TIL LAST +ckuus6.c: Add LINK command for K95 on NT. +ckuus4.c: Support for new K95 directory structure; SSHSUB support +ckuus3.c: Support for new K95 directory structure; some SSH changes +ckuus2.c: Changes to SSH related help text, add HELP LINK text +ckuusr.c: LINK command, SSH OPEN /PASSWORD: /SUBSYSTEM: switches, + Pattern-management fixes. +ckctel.c, ck_ssl.c, ckuath.c, ckcnet.c: + Took Jeff's without looking. +ckuusx.c, ckucns.c, ckucon.c, ckwart.c: + My changes from weeks ago that were never picked up. + +Built OK on Solaris with gcc and on SunOS with (K&R non-ANSI) cc. +31 Jan 2002. + +Meanwhile, Jeff had made various changes in response to Jaya Natarajan at IBM, +whose basic complaint was that numerous failure conditions were not being +detected if the fullscreen file-transfer display was active. Jeff found that +this was because big blocks of code were skipped in that case and changed the +code not to do that, which fixed the reported problems. But later Jaya said +that "ftp mget file1 file2" acted like "ftp mget *", so it seemed that Jeff's +fixes broke file selection. After taking Jeff's fixes for ckcftp.c, however, +I still could not reproduce the problem. ckcftp.c, 31 Jan 2002. <-- Later, +it turned out the problem was with IBM's custom FTP server. + +Fixed updates that I missed yesterday in ckcftp.c, ckuusr.c. Moved misplaced +#ifdef in ckuusy.c breaking nonet builds. Added #ifdefs to sysinit() for +nonet builds in ckutio.c. Ran through build-in-many-configurations script +in Linux, all builds OK. 1 Feb 2002. + +Moved shoesc() definition outside of NOXFER to fix NOXFER builds. +ckuusx.c, 1 Feb 2002. + +Added MYCUSTOM definition alongside KERMRC and changed KERMCL to be the +same as CKMAXPATH, instead of some random hardwired number. ckuusr.h, +1 Feb 2002. + +Changed ckcdeb.h to define DIRSEP and ISDIRSEP(), and put #ifndef +[IS]DIRSEP..#endif around all [IS]DIRSEP definitions in ck[udso]fio.c, so we +can finally put away the many repeated #ifdef chains when we get around to it. +1 Feb 2002. + +Make VMS zkermini() return 1 on success, 0 on failure, rather than 0 always. +ckvfio.c, 1 Feb 2002. + +Added code to doinit(), just before it goes to execute the init file. If the +init file name we are about to open is empty or fails zchki(), substitute the +customization filename. For now this code is in #ifdef USE_CUSTOM..#endif, +which is not defined by default. It does the trick in Unix and VMS. Also +included code from Jeff for K95, but this needs verification and testing. +Also used DIRSEP and ISDIRSEP() throughout doinit() instead of the long #ifdef +chains. ckuus5.c, 1 Feb 2002. + +Moved shoesc() prototype from ckuusr.h to ckcker.h so modules that need it +don't have to include ckuusr.h just for this one thing (example: ckvcon.c). +1 Feb 2002. + +Defined USE_CUSTOM by default, except if NOCUSTOM is defined. ckuusr.h, +1 Feb 2002. + +Fixed kermit-sshsub code to really enter server mode, and to print +"KERMIT READY TO SERVE..." so scripts can wait for it. Also bumped the +C-Kermit test ID to RC.03 and the K95 one to Beta.02. ckcpro.w, ckcmai.c, +2 Feb 2002. + +I was thinking about adding SET COMMAND BUFFER-SIZE to let people allocate +as big a buffer as they wanted at runtime, mainly for defining huge macros. +Moved the SCMD_blah definitions from ckuusr.h to ckuus3.c, since they aren't +used anywhere else. But stopped there since the rest turns out to be a rather +big deal. ckuusr.h, ckuus3.c, 2 Feb 2002. + +From Jeff, 3 Feb 2002: + . Fix an out-of-order modem name in the SET MODEM TYPE table: ckudia.c. + . Use SET LOGIN USER and PASSWORD if present. ckcftp.c. + +Cody Gould noticed that array declarations had become case sensitive, and +upper case didn't work. Diagnosis: misplaced case conversion in xarray(). +Fixed in ckuus5.c, 4 Feb 2002. + +SHOW VAR dumps core on \v(sexpression) or \v(svalue) -- failure to check for +NULL pointer. I wonder why this didn't happen before (answer: because I was +doing it on SunOS; now I'm doing it on Solaris). ckuus4.c, 6 Feb 2002. + +I've had several requests for "show var name name name...". I added this to +doshow(), such that SHOW VAR works exactly as it did before (if you don't give +it an arg, it lists all variables; if you give it an arg, it appends "*" to it +and lists all matching variables) but now you can also give more than one arg +and it works the same way with each one as it did before if you gave it a +single item (i.e., "*" is appended, so "show var os cmd" shows all variables +whose names begin with "os" or "cmd". You can also freely use pattern +notation, including anchors. Hmmm, no, actually it's different in that now +each includes an implied * before AND after, so "show var version" shows all +variables whose name contain "version" rather than all variables whose names +start with it. ckuus5.c, 6 Feb 2002. + +Cody Gould reported that WRITE FILE blah blah \fexec(anything) ... got a +spurious "File or Log not open" error. This turns out to be a rather +pervasive problem -- whenever you use \fexec() it calls the parser recursively +and this can run roughshod over global variables, such as our innocent little +x, y, and s. The fix in this case was to put x and y on the stack. The same +thing probably needs doing in about 10,000 other places. Too bad C isn't +Algol. ckuusr.c, 6 Feb 2002. + +Minor fix to SHO VAR -- the "^" anchor wasn't working (e.g. "show var ^os"). +ckuus5.c, 6 Feb 2002. + +Fixes from Jeff for FTP file-transfer character-set translation in K95 and +in WIKSD, plus updated K95 SSH help text. ckcftp.c, ckcfns.c, ckuus2.c, +7 Feb 2002. + +Server has its date set in the past. Client says "remote dir". Server sends +A packet containing old date. If client has FILE COLLISION UPDATE, it +rejects the directory listing. Changed gattr() to only reject real files +(introduced by F packet), not X-packet material like directory listings. +ckcfn3.c, 7 Feb 2002. + +Up-down arrow keys for command recall. People have been asking for it for +years but now it's actually important because of PDAs that don't have Ctrl +keys. Would have been trivial except that we use getchar() rather than +coninc() for reading from the keyboard in Unix so conchk() doesn't help. In +fact there are lots of other places where conchk() is used this way and works +only by accident. The only reason we never noticed a problem before is that +characters don't usually arrive from the keyboard that fast. But when an +arrow key sends "ESC [ A" all once, the stdin buffer gets some extra stuff in +it, which getchar() will return next time, but which coninc()/conchk() will +never see. So I added a new cmdconchk() routine which, if the keyboard is +being read with getchar() rather than coninc(), looks at the stdin buffer. +Unfortunately, however, there is no API for this, nor is there any standard +way to access the stdin buffer directly. So first I did it for Solaris. Then +to make it portable requires a survey of the headers for every platform. I +found four major variations: + + stdin->_r: + {Free,Open,Net}BSD, BSDI + stdin->_cnt: + SunOS, Solaris, HP-UX 5-6, AIX, VMS, SINIX, IRIX 5.3-6.5, DGUX + 4.2BSD, 4.3BSD, OSF/1..Tru64, QNX4, Unixware 1.0-2.1.0 + stdin->__cnt: + HP-UX 7-11, SCO: OSR5.0.6a, Unixware 2.1.3-7.x, OU8, UNIX 3.2v4.x + Subtract read from end pointer (_IO_file_flags defined): + Linux (tested on RH 5.2 thru 7.1) + +The Linux method is new and different to account for multibyte characters. +All the others assume character == byte. + +For docs: ANSI only, 7-bit only; both application and cursor modes are +accepted. Only up and down arrow are handled; left and right arrows cause +a beep. ckucmd.c, 8 Feb 2002. + +Build-all: Discovered that changing CTTNAM from TT: to SYS$INPUT: in VMS +(which was done on 3 Jan 2002 to work around problems starting Kermit in +batch, spawn'd, etc) breaks Kermit on VMS 5.5/VAX (concb() fails with "lacks +sufficient privilege"; if you enable all privs Kermit starts but then spews +out a constant stream of BEL characters). If you put dftty back to "TT:", +everything is fine -- I have no idea why, so I used #ifdef VMSV70 to decide, +which is totally crude. Next I had to find where the boundary really is: VAX +vs Alpha? VAX C vs DEC C? Or between VMS releases? Built on: + . VMS 6.2 Alpha (DEC C) - OK with TT: + . VMS 6.2 Alpha (DEC C) - OK with SYS$INPUT: <-- keep this one + . VMS 7.1 VAX (DEC C) +So the final condition is #ifdef VMSV60. ckvker.com, ckvtio.c, ckuus5.c. + +QNX 6 needed some attention too: + . Whoever did the makefile target made the default port "/dev/ser1". + . Arrow keys... +But I gave up on getting arrow keys to work -- it should be just like *BSD, +but for some reason gcc complains that struct FILE has no _r member, even +though it does (getchar uses it). + +Checked stdio.h on Mac OS X and it looks like the *BSDs. + +--- C-Kermit 8.0.201 --- + +Removed -g from solaris2xg+krb5+krb4+openssl+shadow makefile target -- it +was producing a 15MB binary! makefile, 14 Feb 2002. + +Fixed a couple thinkos in "make install": $(DESTDIR) should not have been +included in the tests for whether INFODIR or SRCDIR were desired. makefile, +14 Feb 2002. + +(tarball refreshed 16 Feb 2002) + +--- C-Kermit 8.0.201 --- + +From Jeff: Better seeding of \frandom(): ckcmai.c, ckuus4.c, 18 Feb 2002. + +From Jeff: Make arrow keys work in WIKSD, but now also unconditionally +compile arrow-key code in all versions. ckucmd.c, 18 Feb 2002. + +From Jeff: ckuath.c, ck_ssl.c, ckcnet.c (didn't look). 18 Feb 2002. + +Added ORIENTATION command, that lists the various important directories, and +\flongpathname() and \fshortpathname(), which do path format conversions in +Windows, and are just synonynyms for \fpathname() elsewhere. The new functions +need building and testing in Windows. ckuusr.h, ckuus[r24].c, 18 Feb 2002. + +Changed PWD for Windows only to show both short and long paths (but only if +they are different; otherwise it behaves as before). ckuusr.c, 18 Feb 2002. + +Changed default Windows prompt to show long pathname. ckuus5.c, 18 Feb 2002. + +Updated INTRO command to mention FTP, HTTP, and SSH. ckuus2.c, 18 Feb 2002. + +From Jeff: fixes for typos in GetLongPathName() code: ckuus[r4].c, 22 Feb 2002. + +From Jeff: net/auth updates: ckcnet.c, ckuath.c, 22 Feb 2002. + +Added -DUSE_FILE__CNT to NCR MPRAS targets, George Gilmer: makefile, +24 Feb 2002. + +From Jeff: Add support for GetLongPathName() in Win95 and NT: ckcdeb.h, +ckuus[r4].c, 24 Feb 2002. + +From Jeff: More fixes for FTP SIGINT, plus fix [M]PUT /MOVE. ckcftp.c, +24 Feb 2002. + +Fixed an unguarded reference to inserver, gtword(): ckucmd.c, 24 Feb 2002. + +Adapted RETRIEVE for use with FTP connections; this one was missed when +adapting GET, REGET, MOVE, etc. ckuus6.c, ckcftp.c, 24 Feb 2002. + +Added special COPYRIGHT command text for the free version of WIKSD. +ckcmai.c, ckuusr.c, 24 Feb 2002. + +C-Kermit, when in CONNECT mode and given the U sequence, would +unconditionally close the connection if it was a network connection. This +is bad when Telnetting to a modem server. I added to code to prevent this +in the RFC2117 TELNET COMPORT case but I'm not sure how to exend this to the +general case (or whether it would be a good idea). ckucns.c, 24 Feb 2002. + +During file transfer, chktimo() calls ttgspd() for every packet, which clearly +doesn't make sense on network connections, especially since on Telnet COMPORT +connections it results in a network speed query for every packet. Rearranged +the code so this happens only on true serial-port connections. ckcfn2.c, +24 Feb 2002. + +From Jeff: Fix reversed ANSI/non-ANSI function declarations clauses in +ckcftp.c, 26 Feb 2002. + +Changed Unix CONNECT module to call kstart() only when it has a chance of +doing anything (i.e. a Kermit packet has been partially detected, or the +packet start character just came in), rather than unconditionally on every +incoming character. ckucns.c, 8 Mar 2002. + +FTP PUT /SERVER-RENAME:, /RENAME-TO:, /MOVE-TO: were sticky. Patch: In +ckcftp.c, near the top of doftpput(), add the lines marked with "+": + + 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(). */ + +ckcftp.c, 26 Mar 2002. + +\fday() and \fnday() were broken for dates prior to 17 Nov 1858. Fixed in +fneval(): ckuus4.c, 28 Mar 2002. + +From Jeff: + . New calling convenion for demoscrn(): ckucmd.c, ckuusx.c + . Fix for host-initiated 80/132 col screen mode change. ckuus7.c. + . New \v(desktop) variable: K95 user desktop directory, ckuusr.h, ckuus4.c + . New \v(rfc2717_signature) var: Telnet Com Port, ckuusr.h, ckuus4.c + . Uncomment "not-reached" return(-2) in xgnbyte(): ckcfns.c + . New dates: ckcmai.c. + . Telnet Com Port fixes: ckutio.c + . SET PRINTER fixes for K95: ckuus3.c + . Session limit adjustments: ckuus3.c + . New directory layout for K95 (TAKE, ORIENT): ckuusr.c + . Fixes for Telnet Com Port, recycling SSH connections: ckuusr.c + +From me, not picked up by Jeff previously: + . kstart() speedup: ckucns.c. + +1 Apr 2002. + +---K95 1.1.21--- + +From Jeff, 4 Apr 2002: + . More fixes for Telnet Com Port: ckuus4.c, ckudia.c, ckutio.c, ckcnet.c: + . network connections will check for carrier detect if SET + CARRIER-WATCH is ON. This could have a potential conflict if + the option is negotiated and the carrier is off, but the site + requires login. + . modem hangup message generated since the dial module did not + believe that network modems could be reset with a DTR drop. + . Version number adjustments: 8.0.203, 1.1.99: ckcmai.c. + . Security: ck_ssl.[ch], ckuath.c. + +---C-Kermit 8.0.203--- + +From Jeff, 6 Apr 2002: + . Fix typo in HELP REMOTE HOST: ckuus2.c. + . More Telnet Com Port fixes: ckctel.c, ckcnet.c, ckudia.c, ckutio.c + +From Jeff, 9 Apr 2002: + . Fix autodownload problem: ckcfn[2s].c. + +Chiaki Ishikawa reported that in Linux (two different kinds), if you choose +hardware parity, CONNECT, then escape back, the speed can change. I tracked +this down to the following statement in ttvt(): + + tttvt.c_cflag &= ~(IGNPAR); /* Don't discard incoming bytes */ + +Somehow execution of this statement corrupted the speed words of the termios +struct, which are entirely separate words that are nowhere near the c_cflag +member. Anyway, the statement is wrong; it should be: + + tttvt.c_cflag |= IGNPAR; /* Don't discard incoming bytes */ + +Fixing it cured the problem; don't ask me why. ckutio.c, 9 Apr 2002. + +From Jeff: + fixes the problem reported by robi@hastdeer.com.au. The request to + enter server mode was received while we were entering server mode. + But the server was waiting for the response to REQ_STOP sent to the + client. Therefore, we weren't quite in server mode yet and the + request to enter server mode was rejected. A check for the sstate + value solves the problem. ckctel.c, 10 Apr 2002. + +Chiaki Ishikawa (CI) discovered the real cause for the speed changing problem. +I was setting the IGNPAR bit in the wrong flag word: it should have been +c_iflag instead of c_oflag, silly me. Fixed in ttvt() and ttpkt(): ckutio.c. +I also did a thorough census of all the termio[s] flags to ensure each was +applied to the right flag word -- they were, IGNPAR in the HWPARITY case was +the only mistake. CI also discovered that the speed words in the Linux +termios struct are not used at all -- the speeds are encoded in an +undocumented field of c_cflag, which explains the problem. 10 Apr 2002. + +Any use of \{nnn} character notation in a macro definition, loop, or other +braced block caused an "unbalanced braces" parse error. The backslash in this +case is not quoting the open brace; it's introducing a balanced braced +quantity. Special-cased in getncm(): ckuus5.c, 12 Apr 2002. + +The semantics of "if defined \v(xxx)" were changed in 8.0 to avoid obnoxious +error messages when xxx was not a built-in variable (see notes of 19 Nov +2000), such that "if defined \v(xxx)" would always succeed if there were such +a variable, even if it had no value. The behavior that is documented in the +book (and also in ckermit70.html) and that we had in versions 6 and 7, was +that IF DEFINED \v(xxx) would fail if \v(xxx) was defined but had an empty +value OR if it was not defined, and would succeed only if it was defined and +had a value. Fixed in boolexp(): ckuus6.c, 12 Apr 2002. + +What about \function()s? IF DEF \fblah() presently succeeds if the function +exists; you don't even have to give arguments. I think this behavior is more +useful than if I required valid arguments and then evaluated the function -- +you can do that anyway with 'if not eq "\fxxx(a,b)" "" ...' Of course this +argument applies to "if def \v(xxx)" too, except that the current behavior is +consistent with the 7.0 behavior, so there is no need for a change. + +Kent Martin discovered that if a macro contains a LOCAL statement for a +variable whose name is the same as, or a unique left substring of, the macro's +name, then undefining the local variable makes the macro disappear: + + define DateDiff { + echo {DateDiff(\%1) executing...} + } + define Kent { + do DateDiff {2} + local date + assign date {} + do DateDiff {3} <-- This fails (A) + } + do DateDiff {1} + do Kent + do DateDiff {4} <-- So does this (B) + +The first part of the problem is that "assign date {}" called delmac with +exact=0, so delmac evidently deleted first macro whose name started with +"date" -- and since the only one was DateDiff, that's the one that was +deleted. Fixing this (change "delmac(vnp,0)" to "delmac(vnp,1)" in dodef()) +got us past A. The second part was making the same fix to the delmac() +call in popclvl(). ckuus[56].c, 13 Apr 2002. + +The INPUT command ignored the parity setting, thus SET PARITY EVEN, +INPUT 10 "login:" didn't work. Fixed in doinput(): ckuus4.c. Also fixed a +bogus #ifdef COMMENT section that messed up the block structure of the module +and therefore EMACS's indenting. 18 Apr 2002. + +Added sco32v500net+ssl and Added sco32v505net+ssl targets, from Scott Rochford +at Dell (not sure yet if they work). Makefile, 19 Apr 2002. + +From Jeff, 22 Apr 2002: + . Added "darkgray" color and made "dgray" an invisible synonym: ckuus3.c. + . Fix carrier sense on Telnet Com Port immediately after dial: ckudia.c. + . Change krb5_des_blah() arg list: ckutio.c. + . Fix ttgmdm() for Telnet Com Port: ckutio.c. + . Fix tthang() return code: ckutio.c. + . Add aix43gcc+openssl target: makefile. + +From Jeff, 25 Apr 2002: + . Fix SET GUI keyword table: ckuus[37].c. + . A final fix to Telnet Com Port: ckctel.c, ckcnet.c. + +From Jeff, 26 Apr 2002: + . Another final fix to Telnet Com Port: ckctel.c, ckudia.c. + +From Jeff, 27 Apr 2002: + . separate the wait mechanism for TELNET SB COMPORT synchronous messages + from the asynchronous TELNET SB COMPORT MODEMSTATUS messages: ckctel.[ch] + . fix debug messages in Certificate verify functions: ck_ssl.c, ckcftp.c.a + +Frank, 27 Apr 2002: + . Fixed VMS zgetfs() to fail when file doesn't exist: ckvfio.c. + . Fixed UNIX zgetfs() to check for null or empty arg: ckufio.c. + . Added #include for time() call: ckcmai.c. + . Add casts to args in tn_wait() calls: ckctel.c. + +SINIX-P 5.42 (Pyramid architecture) makefile target from Igor Sobrado. +makefile (no source-code changes), 1 May 2002. + +From Jeff, 5 May 2002, + . Fix some "unknown host" messages: ckcftp.c. + . Add more casts to tnc_wait() calls: ckudia.c. + . Improvements to SHOW SSH, SHOW GUI: ckuus3.c. + . Fixes to SET COMMAND { WIDTH, HEIGHT }: ckuus3.c. + . Updates to ck_ssl.[ch], ckctel.c, ckcnet.c. + +Fixed the erroneous setting of ssh_cas during switch parsing rather than +after cmcfm() in setlin(): ckuus7.c, 5 May 2002. + +setlin() decomposition (2300 lines), Part One: + + . Copied a big chunk from the end of setlin(), beginning with net directory + lookup, but only the network-specific and common parts, to a new routine, + cx_net(), 900 lines. + + . Extracted many repetitious lines of error-message code from cx_net() + to a new routine, cx_fail(). Error messages are stored in slmsg, and + also printed but only if we were not called from a GUI dialog (and + QUIET wasn't set, etc etc). Any adjutments in this policy can now be + made in one place. + + . I put a call to cx_net() in setlin() just before all the code it replaced. + It works for TELNET and SET HOST /TELNET. + + . Built with mkwatsol-k5k4ssl; after a couple fixes it builds OK and makes + Kerberized connections OK. + + . Copied the serial-port and common parts of the setlin() post-cmcfm() + code to another new routine, cx_serial(), about 275 lines. Fixed + messages not to come out when called from GUI dialog, etc. Inserted + a call to cx_serial() at the appropriate spot in setlin(). Tested + serial connections on watsun with "set line /dev/ttyh6", works OK. + + . Removed all the code from setlin() that was copied to cx_*(). This slims + setlin() down to 1120 lines. Tested regular Telnet, Kerberized Telnet, and + serial connections again, all OK. The Unix version of the SSH command is + OK too. + +setlin() deconstruction, Part Two: + +Now that we have the common network and serial connection pieces moved out of +setlin(), we still need to move out the little code snippets for each network +type that take place between command confirmation and the common code we just +replaced. As far as I can tell, this needs doing only for SSH. The code +labeled "Stash everything" copied to cx_ssh() but I didn't remove the original +code since I can't test this. I think I'm done -- maybe I'm overlooking +something but I don't know what... First we need to test the heck out of it +in all command-line versions (K95 and C-Kermit). Then to use this from +the GUI, see the calling sequences for cx_serial(), cx_net(), and cx_ssh(): + + . For serial or TAPI connections, the GUI should call cx_serial(). + . For SSH connections, it should call cx_ssh() and then cx_net(). + . For all other network connections, just calls cx_net(). + +ckuus7.c, Cinco de Mayo de 2002. + +New ckuus7.c from Jeff, 8 May 2002. Merge cx_ssh() into cx_net(). Also: I +had made line[] an automatic variable, since the global line[] buffer is used +by almost every parsing routine in C-Kermit to hold string fields between +parsing and execution but Jeff says he found that some code somewhere depended +on line[] containing the hostname after setlin() was finished. + +From Jeff, 10 May 2002: + . Fix SET SSH STRICT-HOST-CHECKING parse: ckuus3.c. + . Add prototypes for cx_net() and cx_serial(): ckuusr.h. + . Add ANSI versions of cx_net() and cx_serial() declarations and supply a + missing parameter in the cx_serial() invocation, change SSHCMD cx_net() + invocation to new form. + +From Jeff, 16 May 2002: + . ANSI strictness changes: ck_ssl.[ch] + . New DIALER command: ckuusr.[ch] + . Correction to how -0 turns off autodownload: ckuusy.c + . Prototypes for GUI menu action functions: ckuusr.h. + . Replace setting of GUI-action variables by function calls: ckuus[3457x].c + . Fix FTP -z switch parsing: ckcftp.c. + . Fix SET HOST testing of setlin() return code: ckuus3.c + +From Jeff, 18 May 2002: + . Allow half-size GUI fonts: ckuus[35y].c. + +Fixed setguifont() to parse fractional font sizes and round to nearest half +point. ckuus3.c, 18 May 2002. + +For GUI, wrote front ends for getyesno(), readtext(), and readpass(): + + . uq_ok() prints text and gets Yes/No, OK/Cancel, or just OK response. + This replaces getyesno() and can also be used for alert or help boxes. + + . uq_txt() prints text and gets a single text response. Replaces + readtext() and readpass(). + + . uq_mtxt() is like uq_txt() but allows multiple text fields. Replaces + any combination of readtext() and readpass(). + +Obviously the #ifdef KUI portions of the uq_blah() routines need filling in. +ckuusr.h, ckuus3.c, 18 May 2002. + +Converted selected getyesno() calls to uq_ok(): ckcftp.c, ckuus3.c, ckuus6.c. +Some were not converted because it was inappropriate, e.g. DELETE /ASK; others +because they're in Jeff's code. The most interesting conversions are in the +DIAL command when DIAL CONFIRMATION is ON. Here there is a dialog for each +phone number asking if it's OK (ug_ok()) and if not, asking for a replacement +(uq_txt()); seems to work fine in C-Kermit. All the candidates for uq_mtxt() +are in Jeff's code. 18 May 2002. + +From Jeff: Convert remaining getyesno/readtext/readpass calls to uq_blah() +so they can be GUI dialogs. ckuus[37].c, ckcftp.c, ckuath.c, ck_ssl.c, +21 May 2002. + +Added KCD command = CD to symbolic directory name (EXEDIR, COMMON, APPDATA, +TMPDIR, etc etc). ckuusr.h, ckuus[r25].c, 21 May 2002. + +From Jeff, 28 May 2002: + . --title: commandline option: ckuusr.h, ckuusy.c + . Fix some #includes, move some declarations: ckcfns.c + . Change K95 version from Dev.00 to Beta.01 + . ASK[Q] /GUI: ckuus6.c. + . Various GUI screen updates and #ifdefs: ckuus7.c + . Add missing cx_net() calls to new setlin() for file SuperLAT..: ckuus7.c + . Updated uq_*() routines for GUI dialogs: ckuus3.c. + +Added GETOK switches (/TIMEOUT for all; /POPUP and /GUI for K95G): +ckuus6.c, 29 May 2002. + +Added HELP SET GUI text. ckuus2.c, 29 May 2002. + +From Jeff: + . Another K95-specific #include for ckcfns.c. + . More items for K95G Actions menu. + . Change K95G Locus switching to call setlocus() rather than set variable. + . Ditto for several other variables now settable from Actions menu. + . Fix SET HOST /NET:SSH status code so IF SUCCESS works. + . Fix SHOW SSH port-forwarding. +ckcfns.c, ckuus[r367].c, ckcftp.c, ckcmai.c, 30 May 2002. + +Changed SET LOCUS to have a new value, ASK, corresponding to new autolocus +value of 2, K95G only. Changed setlocus() to not do anything if the new and +old loci are the same, otherwise to invoke a GUI dialog in K95G if autolocus +is 2, and also to handle any text messages. Changed SHOW COMMAND to show ASK +value for SET LOCUS. Rewrote HELP SET LOCUS. ckuusr.[ch], ckuus[23].c, +ckcftp.c, 30 May 2002. + +Add a missing space to Locus popup, and fix Jeff's version of the code to +compile in C-Kermit. ckuusr.c, 31 May 2002. + +From Jeff, for K95 GUI, 6 June 2002: + . Force some GUI popups to be in foreground: ckuus3.c. + . Fix SHOW TERM font display: ckuus5.c. + . Update K95 version numbers and date (4 June 2002): ckcmai.c. + . Add note about encrypted private keys vs scripts to HELP SET AUTH: ckuus2.c. + . Fix SET HOST for DECnet: ckuus7.c. + +--- K95 2.0 --- + +From Jeff, 7 June 2002: + . Fix some #ifdefs for Unix builds (locus, dial, etc): ckuus7.c + . Add gui_resize_scale_font() prototype: ckuus3.c + . Add some missing SET GUI commands: ckuus3.c + . Update version numbers: ckcmai.c + +--- K95 2.0.1 --- + +From Jeff, 11 June 2002: + . Conditionalize Locus-switching popup text for GUI/Console: ckuusr.c. + . Fix the SRP_installed_as_server() function. The new API returns TRUE even + if the SRP config and password files cannot be found. Went back to the old + API. This bug affects C-Kermit 8 when built with SRP as well as 1.1.21 + through 2.0.1. Since iksdnt.exe has not been shipped yet I fixed it and + uploaded a new non-beta build of it. ckuath.c. + +From Jeff, 12 June 2002: + . Fix SSH AGENT ADD: ckuusr.c. + . Fix --facename: option to not fail if name unknown: ckuusy.c. + . Fixes for OpenSSL 0.9.7 and OpenBSD 3.1: ck_ssl.c. + . Fix SET AUTH TLS VERIFY NO to prevent a dialog but still a warning if + SET AUTH TLS VERBOSE ON is set: ck_ssl.c. + . Fix FTP code to verify the hostname as specified by the user and not + the hostname discovered by the reverse DNS lookup. For example, + FTP OPEN kermit.columbia.edu + should produce a dialog because that name is not in the certificate + even though ftp.kermit.columbia.edu (the reverse DNS name) is: ckcftp.c. + +Add support for Solaris 9 and NetBSD 1.6. makefile, ckuver.h, ckcdeb.h, +13 Jun 2002. + +Discovered that Solaris 9 wants to hide the members of struct FILE, and +enforces this for 64-bit builds. They offer some functions like __fbufsize() +to get the info, but not the info we need for reading escape sequences (the +_cnt member). Let's hear it for political correctness. Created new solaris9g +(32-bit) and solaris9g64 (64-bit) targets. Sorry, no arrow keys in 64-bit +mode. Also no more direct access to sys_errlist[]; must use strerror(). +makefile, ckucmd.c, 13 Jun 2002. + +Added solaris9g+openssl+zlib+pam+shadow, which in turn required adding +solaris2xg32+openssl+zlib+pam+shadow, needed for gcc 3.1 in which you have +to specify 32-bit. Fails for some mysterious reason in link step +(can't find libssl.so.0.9.6 even though it's there). makefile, 13 Jun 2002. + +Solaris 8 empty socket problems again -- tthang() times out, subsequent +tcsetattr() calls do horrible things. Added a bandaid to ttclos(): don't +call tcsetattr() any more if hangup timed out. ckutio.c, 14 June 2002. + +Gerry B reported the bandaid got us bit farther but Kermit still disappears. +Added code to reassert the alarm signal handler, since it is likely that +Solaris has become stricter about this since last time I looked. (Later +Gerry reported back that this did the trick -- C-Kermit now exits normally +and releases the lockfile). ttclos(): ckutio.c, 17 Jun 2002. + +If you use Kermit to copy a file to a destination file that already exists and +is longer than the source file, the destination file is not truncated. I had +mistakenly assumed that setting O_CREAT in the open() call in zcopy() would +create a new copy of the file. Fixed by also setting O_TRUNC. ckufio.c, +17 Jun 2002. + +Updated HELP INPUT and MINPUT text to explain 0 and -1 timeout values, and +HELP DIAL to explain about entering CONNECT mode automatically. ckuus2.c, +17 Jun 2002. + +Got rid of client-side "Press the X or E key to cancel" message when giving +a REMOTE command if QUIET is set or if XFER DISPLAY is NONE. ckuus7.c, +17 Jun 2002. + +From Jeff 25 Jun 2002: + . Add SUN terminal type: ckuusr.h, ckuus[57].c. + . Add GUI file transfer display: ckcker.h, ckuus[47x].c. + . Changes to allow C-Kermit to build with OpenSSL 0.9.7. Current + C-Kermit code is designed to compile with 0.9.6 and earlier. To + compile with 0.9.7 you must specify -DOPENSSL_097. This avoids + missing symbols in the DES library. The functions in OpenSSL were + renamed in 0.9.7 to avoid link time conflicts with Kerberos 4. + ckufio.c ck_crp.c ckuath.c ck_ssl.h ck_ssl.c, makefile. + +From Jeff 26 Jun 2002: + . apparently the SSL Passphrase Callback function was not converted + from readpass() to uq_txt() + . FTP Authentication failure errors were not being reported to the + user. So a failure would appear to be a successful completion + unless FTP DEBUG was ON. Now the message is reported unless + the QUIET flag is set. +ck_ssl.c, ckcftp.c. + +SET TRANSFER MODE MANUAL didn't work for FTP; fixed in putfile() and getfile(): +ckcftp.c, 1 Jul 2002. + +Changed debug log for FTP to log "FTP SENT" and "FTP RECD" for protocol +messages, just like we do for Telnet, to make it easy to grep them out of +the log. ckcftp.c, 1 Jul 2002. + +In FTP MGET /UPDATE, equal times spuriously caused download. doftpget() was +misinterpreting chkmodtime()'s return code. ckcftp.c, 3 Jul 2002. + +In FTP MGET /RECOVER, recovery is skipped if the local file is newer than +the remote. This would seem to make sense, but when a download is +interrupted, the partial file never gets the date of the remote file, so +the partial file is always newer, and recovery never works. Fixed in +recvrequest() by commenting out the date check. ckcftp.c, 3 Jul 2002. + +A better way to fix the previous problem is to always set the file date from +the server and then only allow /RECOVER to work when the dates are equal. +But that's not possible because MDTM is not implemented universally, and it +conflicts with how Kermit currently works, namely that FTP DATES are OFF by +default. Also, checking dates prevents [M]GET /RECOVER from working with +files that were incompletely downloaded by some other FTP client. + +In FTP MGET /RECOVER ..., the first file in each group +is always downloaded. Diagnosis: Kermit sends "TYPE A" prior to NLST (as it +must). Then when it sends its first SIZE command, it's still in ASCII mode, +so the server sends the "ASCII size" rather than the binary size, which does +not agree with the size of the local file (which was downloaded in binary +mode), so recovery is always attempted even when the files are identical. The +TYPE A command is sent by initconn(). After the remote_files() call, we have +to change the type back to the prevailing type before sending the first SIZE +command. Fixed in cmdlinget() and doftpget(): ckcftp.c, 3 Jul 2002. + +In FTP MGET /EXCEPT: used with SET XFER DISPLAY brief, files that +are skipped just say ERROR instead of saying why they were skipped. Fixed +in doftpget(): ckcftp.c, 3 Jul 2002. + +Added EXIT to top-level HELP text. ckuus2.c, 13 Jul 2002. + +Strip braces in REINPUT n {string}. ckuusr.c, 13 Jul 2002. + +Added /QUIET switch to ASK-class commands. This means not to print any error +messages when an ASK-class command times out waiting for a response. Made +sure that when a timeout occurs, the command fails. Also made sure the +c-Kermit prompt doesn't write over the ASK prompt if ASK times out. Also +fixed ASK, when it times out, not to return -9, which it did in one case, +which causes a command-stack dump. ckuus[267].c, ckucmd.c, 13 Jul 2002. + +Fixed SET FILE INCOMPLETE help text, which said that both KEEP and AUTO were +the default. ckuus2.c, 13 Jul 2002. + +If you SET FTP DEB ON and then turn it OFF, the MGET temp file is still kept. +Fixed by getting rid of ftp_knf variable and using ftp_deb to control whether +temp file is deleted (ftp_knf was being set from ftp_deb anyway, but then +wasn't being reset by SET FTP DEB OFF). ckcftp.c, 13 Jul 2002. + +If an FTP transfer was in progress but the FTP connection drops and automatic +locus switching is enabled, the locus does not change; thus (for example) a +subsequent DELETE command makes Kermit send a REMOTE DELETE packet on stdout. +Fixed in lostpeer(): ckcftp.c, 13 Jul 2002. + +For docs: FTP CD with no arg might not be accepted by the server; e.g. the +Kermit FTP server says "501 Invalid number of arguments". + +The FTP module never handled SET INCOMPLETE. Fixed in doftprecv2(). ckcftp.c, +13 Jul 2002. + +When FTP DATES is ON, we set an incoming file's date only if the file was +received successfully. Changed the code to set the file's date even if it was +received only partially (assuming we can get the date from server). ckcftp.c, +13 Jul 2002. + +Suppose we were doing FTP MGET /UPDATE from a server directory of 100,000 +files. Kermit would send a SIZE command for every file unconditionally. On +some connections, e.g. to the Red Hat Rawhide server, each one could take up +to 30 seconds. That would be 3 million seconds = 34 days. Don't send a SIZE +command during the selection phase unless a /SMALLER or /LARGER selector was +given. Once the file is selected, send a SIZE command only if one hadn't been +sent for that file already. ckcftp.c, 13 Jul 2002. + +Made [M]GET and [M]PUT /UPDATE switch imply FTP DATES ON, since they didn't +work unless it was. ckcftp.c, 13 Jul 2002. + +Added FTP [M]GET /DATES-DIFFER, which is like /UPDATE except it selects files +that are newer or older, rather than only newer. This allows updates from +sources where files might be rolled back to earlier versions. It's a bit +dangerous if you use it without knowing what it's for, since it allows older +files to overwrite newer ones. (Code is also in place for [M]PUT +/DATES-DIFFER, and it works, but I commented it out because it's either +useless or dangerous since when uploading, you can't set the the file dates +when they are arrive on the server.) ckcftp.c, 13 Jul 2002. + +Changed chkmodtime() to remember if MDTM fails on a particular connection +because it's an unknown command (500, 502, or 202), and if so, not to ask +again. ckcftp.c, 13 Jul 2002. + +With this last change, I think it's safe to change the default for FTP DATES +from OFF to ON. ckcftp.c, 13 Jul 2002. + +Increased max number of /EXCEPT: patterns from 8 to 64 for file transfer (not +necessarily for other things). This is now a compile-time symbol NSNDEXCEPT. +ckcker.h, ckcmai.c, ckclib.c, ckcfns.c, ckcftp.c, ckuus[rx].c. 13 Jul 2002. + +Fixed FTP MGET to not send SIZE command when there is a name collision and +FILE COLLISION is DISCARD, even if /SMALLER or /LARGER were also specified. +ckcftp.c, 15 Jul 2002. + +MGET fails if no files were transferred, even if the reason is that no files +met the selection critieria: /COLLISION:DISCARD, /UPDATE, /SMALLER, etc. +Changed MGET to succeed in that case. domget(): ckcftp.c, 16 Jul 2002. + +Big problems with canceling MGET; Ctrl-C cancels the current file, but we +don't break out of the file loop, we just go on to the next file. Worse, if +we're executing a command file that has a series of MGETs, Ctrl-C doesn't +break us out of the command file. Fixed by making failftprecv() and +failftprecv2() "chain" to the main SIGINT handler, trap(). This is fine in +Unix, but I'd be really surprised if it works in K95 so I put it in #ifndef +OS2. Ditto for MPUT: Added the same treatment to failftpsend() and +failftpsend2(). Ditto for cmdcancel(). To adapt to K95, search for "TEST ME +IN K95" (5 places). ckcftp.c, 16 Jul 2002. + +Fixed previous fix to account for the fact that failftpblah() can be called +not only upon Ctrl-C, but also if transfer interrupted with X or Z. +ckcftp.c, 16 Jul 2002. + +Yesterday's fixes revealed another problem: Interrupt MGET with Ctrl-C, start +another MGET, and the file list is total garbage. Diagnosis: secure_getc() +and secure_getbyte() use internal static buffer pointers. The only way they +ever get reset is when the data connection is closed by the server, so if you +interrupt a GET, the pointers are not reset and the next network read (e.g. of +an NLST response) returns whatever junk was lying around in the old buffer. +ckcftp.c, 17 Jul 2002. + +FTP MGET temp file is kept only if FTP DEBUG is ON. Changed FTP module to +also keep it if the regular debug log is active. ckcftp.c, 17 Jul 2002. + +Fixed version test in ckermit.ini: should be 6 digits, not 5. 17 Jul 2002. + +Changed C-Kermit version number to 8.0.205 so scripts can test for the +recent changes. ckcmai.c, 18 Jul 2002. + +---8.0.205--- + +SET FILE COLLISION UPDATE would unset FTP DATES due to a typo in the recent +changes. ckcftp.c, 21 Jul 2002. + +FTP [M]GET /DATES-DIFFER really should have been a collision option. Added +this option (implemented for FTP only) to both SET FTP COLLISION and the +FTP [M]GET /COLLISION: table, so this way if you have lots of [M]GETs, you +don't have to put /DATES-DIFFER on each one. ckcker.h, ckcftp.c, 21 Jul 2002. + +"FTP MGET a* b* c*" would fail to get any c*'s if no b*'s existed. +ckcftp.c, 21 Jul 2002. + +From Jeff, 22 Jul 2002: + . Beginnings of Ann Arbor Ambassador terminal emulation for K95; + ckuus[57].c, ckuusr.h. + . Bump K95 version number to 2.0.2: ckcmai.c + +Added -DCK_PAM -DCK_SHADOW to all Solaris targets, 2.6 and above. makefile, +23 Jul 2002. + +Discovered that CK_SCRIPTS path search for TAKE files was #ifdef'd out +except for K95. Fixed in ckuusr.c, 25 Jul 2002. + +From Jeff: changes to support K95 italics: ckuus[57].c, 25 Jul 2002. + +Fixed path search for TAKE to not search the CK_SCRIPTS path if the filespec +contains any directory or path parts. Added a new function to check for +this: int hasnopath(filespec) in ckucmd.c: 26 Jul 2002. + +Update HP-UX build instructions from PeterE: makefile, 26 Jul 2002. + +Commented out "const" from struct pam_message declarations because it +causes "initialization type mismatch" warnings. ckufio.c, 26 Jul 2002. + +Suppose you have a network directory containing a listing for host "foo": + + foo tcp/ip foo.bar.com + +Then in K95 you give a command "set host /network-type:ssh foo". This +results in the directory lookup replacing the "ssh" network type with TCP/IP, +and making a Telnet connection. Fix attempted at about line 8625 of ckuus7.c +in cx_net(); needs testing in K95. 26 Jul 2002. + +FTP Password: prompt in Unix was not allowing editing. The code looked right; +I put in some debugging and suddenly it worked. Took out the debugging and +it still worked. Maybe I dreamed it. Anyway, I fixed the "FTP SENT" debug +log entry to not record the password, and removed a redundant section above +to log the same thing, but prior to any charset conversion. ckcftp.c, +27 Jul 2002. + +From Jeff, 28 Jul 2002: + . Fix typo in initxlist(): ckcmai.c. + . Fix typo in Friday's set-host fix: ckuus7.c. + . Move parsing of --height/width command-line args after prescan(): ckuusy.c. + +Added invisible top-level SITE and PASSIVE commands for FTP as a convenience +for habituated FTP client users. ckuusr.[ch], ckcftp.c, 28 Jul 2002. + +A while back a user asked if it was possible to MGET a bunch of files from +an FTP server and have them all appended to each other upon arrival. The +obvious way to do this would have been: + + mget /collision:append /as-name:bigfile *.* + +But to make this work, I had to get rid of the "as-name must contain +variables" check in the MGET parser. doftpget(): ckcftp.c, 28 Jul 2002. + +Verified that it was possible to do the same thing (GET a bunch of files +and append them all into one result file) with Kermit protocol. It works +fine but in this case there is no /COLLISION switch; you have to SET FILE +COLLISION APPEND first. 30 Jul 2002. + +Changed COPY /APPEND to allow wild source to single destination file, e.g. +"copy /append *.* bigfile". ckuus6.c, 30 Jul 2002. + +From Mark Berryman: a replacement for zchkpath(), the VMS routine that checks +whether a file is in the current directory; the old one (that I wrote) was +a hack that only worked sometimes. Martin Vorlaender verified Mark's code in +the situation where mine was breaking (server running in captive account). +ckvfio.c, 30 Jul 2002. + +PeterE reported a problem with SWITCH case labels that start with '#': +The problem is that the SWITCH variable contents in this case happens to be +a comment, e.g.: + + CMD(M)[_forward # Stand: 24.07.2002] + +so the GOTO target is null. The solution would be for SWITCH to put the GOTO +(_FORWARD) target in quotes. But GOTO does not strip quotes or braces from +around its target. Fixed in ckuusr.c, 30 Jul 2002. + +Fixed the SWITCH macro definition to put the _FORWARD target in quotes. +ckuus5.c, 30 Jul 2002. + +PeterE also reported that an empty SWITCH case label did not work. There's no +particular reason why it should, but after a brief look, it wasn't that hard +so I did it. It required commenting out the check for empty labels and fixing +the comparison in dogoto(). Now it's possible to read lines from a file and +use each line as a SWITCH variable, with patterns as case labels, including an +empty label to match empty lines, #* labels to match comment lines, etc. +ckuus[r6].c, 30 Jul 2002. + +PeterE also reported the value of \%* acquiring a trailing blank when +referenced inside a SWITCH statment. This happens because \%* is formed using +\fjoin() on the \&_[] array based on its dimension, and at some point the +dimension is spuriously increased by one. As a workaround, I made \fjoin() +ignore trailing empty \&_[] array elements and oddly enough this also fixed +the growing dimensions problem. The many script torture tests reveal no ill +effects, so it seems like a keeper. ckuus4.c, 30 Jul 2002. + +Some of Peter's sample scripts made C-Kermit 8.0.201 dump core, but no more. + +Fixed "delete xxx" to print an error message and fail if if xxx does not exist. +Ditto for when xxx is a directory. ckuus6.c, 30 Jul 2002. + +Patches to SSL modules from Jeff based on yesterday's advisory. ck_ssl.[ch], +31 Jul 2002. + +Fixed some typos affecting the filename collision action during command-line +FTP [M]GET. ckcftp.c, 31 Jul 2002. + +Fixed SHOW FTP to handle FTP COLLISION DATES-DIFFER. ckcftp.c, 31 Jul 2002. + +A while back someone pointed out that SET CONTROL UNPREFIX ALL and SET +PREFIXING NONE gave different results. Fixed them to use the same code. +Also made "set prefixing none" visible. ckuus3.c, 4 Aug 2002. + +Added SET CD HOME , to let the user specify which directory is intended +when "CD" or "KCD" is given by itself. This is because in Windows, some +applications set up their own HOME environment variable that isn't necessarily +where the user wants "cd" to go, but redefining HOME can interfere with the +application (example: Windows EMACS). SET CD HOME was done by adding a myhome +variable, initially a NULL pointer, and then changing homepath() to use it if +it is set. zhome() is not affected. Also the homepath() prototype had been +missing from header files. ckcmai.c, ckuusr.h, ckuus[2345].c, 4 Aug 2002. + +PeterE got another core dump with his SWITCH statement. Found a place where +an out-of-bounds array reference could occur if the switch variable was +empty. ckuus6.c, 5 Aug 2002. + +PeterE noticed that if the switch variable contained a comma, spurious matches +could occur with the label pattern. The real problem turns out to be what +happens when the SWITCH variable doesn't match any of the case labels and +there is no DEFAULT label. Fixed by having dogoto() in the SWITCH (_FORWARD) +case pop the command stack before returning failure, i.e. by moving the +"if (stopflg) return(0);" statement down a few lines. ckuus6.c, 5 Aug 2002. + +PeterE noticed that a SWITCH case label of :* did not match an empty SWITCH +variable. Fixed in doswitch(): ckuus6.c, 6 Aug 2002. + +In testing the previous fix, I found it only worked sometimes. Inspection +of the debug log showed that a statement like: + + if (y == -3) s = "{}"; + +was assigning "{" rather than "{}" to s. Replacing the string constant by a +buffer containing the same string fixed it. The reason (guessed correctly by +PeterE) was the following sequence: + + y = cmfld("Variable name","",&s,xxstring); + if (y == -3) s = "{}"; + len = ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); + +brstrip() (by design and as documented) affects the string in place. But in +this case the string is a constant, not data in a buffer, so all further uses +of "{}" get the wrong string (at least in optimized builds). The only real +cure is to change brstrip() to make a copy of its argument if it has to do +anything to it. This will slow down some scripts, but it's too risky to +leave it as it was. ckclib.c, 6 Aug 2002. + +The previous change required an audit of the C-Kermit code to make sure that +no references to brstrip() depended the result buffer being persistent, or the +result pointer indicating a position in the source buffer. Oops, it turns out +that thousands of places rely on brstrip() working in place. Therefore the +change had to be undone. There's no good way to write a dummy-proof brstrip(); +programmers either have be sure they're not calling it with a pointer to a +string constant, or else they have to copy the result back to the right place +each time. Better to leave it as it was and audit the code to fix any calls +that refer to string constants (turns out there were only two). Restored the +original fix to doswitch() (replacing the string constant by a buffer holding +the same string), plus minor fixes to ckcftp.c, ckuus[r36].c, 6 Aug 2002. + +We need file dialogs in several situations in the K95 GUI. I added a "user +query" routine for this, uq_file(), in ckuus3.c, filling it in only for Unix. +Then I added code to call it from rcvfil() when (a) it's an autodownload, and +(b) SET TERM AUTODOWNLOAD is ASK (I just added this option; it needs to be set +to see it in action -- maybe it should be the default for KUI, in which case +initialize "int autodl = ?" to TAD_ASK in ckcmai.c). Works fine, except of +course it interferes with the file-transfer display, but that won't be a +problem in K95G. ckuusr.h, ckuus[37].c, ckcfns.c, ckucns.c, 6 Aug 2002. + +Another place we need a file dialog is when Kermit is a URL interpreter. The +problem is: how can we let the user decide whether Kermit should ask? There +really isn't any way. Either it always asks or it never does. In this case I +think it makes sense to always ask if it's KUI, otherwise never. I added the +code for ftp: URLs to to doftprecv2(), which I tested successfully in Unix +before putting it into #ifdef KUI..#endif. Also added code for http[s] to +ckuusy.c in #ifdef KUI..#endif, not tested. + +Still need this added for K95G Actions->Capture. The clearest example is the +FTP one. Just search for KUI in the FTP module. + +Some minor adjustments to yesterday's work, mainly just comments, plus +generate the full pathname for the default file. ckuus3.c, ckcftp.c, +7 Aug 2002. + +Note: for some reason cmofi() is not supplying the default value if user +enters an empty name... (but that won't affect the Windows version). + +Added /USER: and /PASSWORD: switches to SET TCP { HTTP-PROXY, SOCKS-SERVER }. +ckuus3.c, 7 Aug 2002. + +New 'uninstall' target from PeterE, works by having the 'install' target +write an UNINSTALL shell script. makefile, 8 Aug 2002. + +Added some debugging statements to the VMS communications i/o module to try +to track down a problem that occurs when the controlling terminal is a LAT +device. ckvtio.c, 10 Aug 2002. + +Fixed the non-K95 uq_file() to respect the given default name, but still show +the fully qualified absolute pathname for the default in the dialog. The +reason to not use the fully qualifed name as the default in the cmxxx() calls +is that this can easily result in a whole directory tree being created due to +directory aliases, symlinks, etc. So when you get a file by referring to its +URL (e.g. ftp://kermit.columbia.edu/kermit/READ.ME), uq_file() converts the +READ.ME part to (e.g.) /home/fdc/tmp/READ.ME but gives just "READ.ME" as the +default when parsing the name. This way the user knows where it will go and +gets an opportunity to change it, and if the default is accepted, it goes into +the current directory. uq_file(): ckuus3.c, 10 Aug 2002. + +Found the spot for calling uq_file() for kermit:// URL downloads. Added +prefatory text to filename prompts for Kermit and FTP downloads. ckcfns.c, +ckcftp.c, 10 Aug 2002. + +Now with kermit:// or ftp:// URL downloads there's no way to disable the +prompting. I could easily make SET TERMINAL AUTODOWNLOAD ASK cover these +cases too (even though "terminal" has nothing to do with FTP or URL +downloads). OK, I did this, but now prompting is disabled by default. +ckcftp.c, ckcfns.c. 10 Aug 2002. + +Enabled file prompting (adl_ask) by default in K95G, disabled it by default +everywhere else. So now FTP and Kermit URL downloads as well as terminal-mode +Kermit (but not Zmodem) downloads are prompted for if TERMINAL AUTODOWNLOAD is +ASK, which is it by default only in K95G. But this will happen only if +uq_file() is filled in for K95G; otherwise everything should work as before. +ckcmai.c, 10 Aug 2002. + +Notes: + . Need a better command to control this. + . FTP URL downloads are almost instantaneous, whereas Kermit URL downloads + take a really long time to set up (logging in takes at least 10 seconds). + +From Jeff, 13 Aug 2002: + . Increase K95 version to 2.1.0: ckcmai.c. + . SET TCP { HTTP-PROXY, SOCKS-SERVER } /USER: /PASSWORD: actions: ckuus3.c. + +From PeterE: a new install target that's only about half as a big as the +previous one, yet still generates an UNINSTALL script. makefile, 13 Aug 2002. + +Vace wanted to be able to give the FTP client an offset for the server time, +in case the server's time (or timezone) is set incorrectly. I added this by +building on all the date/time parsing/arithmetic code -- notably delta times +-- that was done for C-Kermit 8.0. The new command is SET FTP +SERVER-TIME-OFFSET delta-time; shows up in SHOW FTP and HELP SET FTP. +ckcftp.c, 13 Aug 2002. + +Fixed HELP ASK and HELP GETOK text. ckuus2.c, 14 Aug 2002. + +Fixed GETOK to accept /GUI switch even in K95.EXE and C-Kermit, just like ASK +does (in which case, it just ignores it). ckuus6.c, 14 Aug 2002. + +SET XFER CHAR TRANSPARENT no longer disables character-set translation because +file-scanning turns it back on. The "new way" to disable character-set +translation is SET XFER TRANSLATION OFF. This needlessly confuses users who +expect the old way to still work. So I fixed SET XFER CHAR TRANSPARENT to set +XFER TRANSLATION OFF, and SET XFER CHAR anything-else to set it back ON. +ckuus3.c, 15 Aug 2002. + +Fixed SET TERM AUTODOWNLOAD { ON, OFF } to turn off the ASK flag (adl_ask). +ckuus7.c, 16 Aug 2002. + +Added FEAT query to FTP client from draft-ietf-ftpext-mlst-13.txt. FEAT is +sent along with REST 0, MODE S, and STRU F if /NOINIT is not included in the +FTP OPEN command. Parsing the FEAT result is handled by turning the "auth" +argument to getreply() into a function code: GRF_AUTH to parse AUTH reply; +GRF_FEAT to parse FEAT reply. For GRF_FEAT, getreply() fills in a flag array, +sfttab[] (server feature table); sfttab[0] > 0 means server responded to the +FEAT query, in which case individual elements are set > 0 for each supported +feature. ckcftp.c, 18 Aug 2002. + +If server sends a feature list, display it if FTP DEBUG is on, then set mdtmok +and sizeok (the flags that say whether it's OK to send MDTM and SIZE commands) +accordingly. If user gives an [M]PUT /RECOVER command and server has +announced it doesn't support REST, print a warning but try anyway (maybe +change this later). Responses about other features that we use such as AUTH +and PBSZ are ignored for now -- i.e. we try them anyway. And of course +responses for features we don't care about (LANG, TVFS, PROT) are ignored. +ckcftp.c, 18 Aug 2002. + +If the server says it supports MLST, use MLSD instead of NLST to get the file +list. This is done in remote_files() with some simple string-twiddling. Then +replace the relevant (but not all) SIZE commands with code to first check if +we already got the size from the MLSD response and use that instead rather +than asking again. Same deal for MDTM. ckcftp.c, 18 Aug 2002. + +Checked that this works when giving pathnames in the MGET filespec. Checked +to make sure everything works as before with servers that don't support FEAT +or MLSD. Checked to make sure FTP OPEN blah /NOINIT worked with servers that +do support FEAT and MLSD. Checked that FTP CHECK works. It's all OK. + +Tested only with Ipswitch server; need to find and test with others. + +The stack of temp files needed for MGET /RECURSIVE is annoying because what +if we run out of file descriptors... But the spec doesn't provide a way to +request a recursive listing. + +Supplied a missing comma in HELP SET CD text. ckuus2.c, 19 Aug 2002. + +Generalized parsing of MLST/MLSD file facts and values. Got file type from +server and had MGET skip non-regular files. ckcftp.c, 19 Aug 2002. + +Kirk Turner-Rustin reported that if Unix C-Kermit has a SET +HOST PTY connection (e.g. SSH) open, local window size changes are not +propogated through the connection to the host. I imagine that must be because +the SIGWINCH signal is caught by Kermit and its children don't see it; maybe +if I pass it along to the child fork, all will be OK. Began by exporting +"slavepid" from the pty module and changing its name to pty_fork_pid. Moved +the SIGWINCH handler, winchh(), from ckctel.c to ckutio.c. Armed it from Unix +sysinit() so it's always armed. This way window changes affect Unix C-Kermit +no matter what mode it's in: tt_rows, tt_cols, cmd_rows, and cmd_cols are all +kept in sync. Then if we're not in remote mode (i.e. we have a ttyfd), we +call tn_snaws() and rlog_snaws() (which should be ok since they return right +away if the appropriate kind of connection is not open) and then if +(pty_fork_pid > -1), a SIGWINCH signal is sent to it. ckupty.c, ckctel.c, +ckutio.c, 20 Aug 2002. + +All this works fine except the PTY part; in other words, the original problem +is not fixed. The "kill(pty_fork_pid,SIGWINCH)" call executes without error +but has no effect because the size of the PTY never changed. To make this +work I had to add an ioctl() to change the size of the PTY before sending it +the SIGWINCH. Compiles and works ok on Linux and Solaris; Kirk also confirmed +it for AIX 4.3.3. ckutio.c, 20 Aug 2002. + +Fixed xlookup() to work for uppercase keywords. ckucmd.c, 20 Aug 2002. + +Fixed FTP parsefeat() and parsefacts() to use xlookup() instead of lookup(), +since abbreviated keywords are not allowed. ckcftp.c, 20 Aug 2002. + +Adjusted some lines from yesterday's window-size code for platforms I hadn't +tried yet. ckutio.c, 21 Aug 2002. + +EXIT from K95 when it has an FTP connection open and it pops up the +Locus dialog. Made it not do this if it knows it's in the act of EXITing. +ckuus[rx].c, 22 Aug 2002. + +In K95, FTP GET in ASCII mode results in a file with Unix line terminators +even though the protocol is correct: + + RETR smjulie.txt + 150 Opening ASCII mode data connection for smjulie.txt (1878 bytes). + +The source file is a regular Unix text file with LF at the end of each line. +It's incredible that nobody noticed this before. It only came to light when +somebody tried to open a downloaded text file with Notepad, which doesn't +handle Unix-format files (Wordpad and Emacs have no problems with them). The +problem was in doftprecv2() in the FTT_ASC section. There was no conditional +code for Unix vs Windows. In all cases, the code discarded incoming CR's in +ASCII mode. I put the CR-discarding code in #ifdef UNIX..#endif. ckcftp.c, +22 Aug 2002. + +Removed super-verbose debugging from gtword(): ckucmd.c, 23 Aug 2002. + +Gregory Bond reported a problem with "if defined \$(BLAH) ..." inside of a +SWITCH statement. It wasn't really the SWITCH that was doing it, it was the +fact that he had enclosed the SWITCH case in braces, which made it an +"immediate macro" (XXMACRO). The XXMACRO code parsed the macro definition +(the part inside the braces) with cmtxt(...,xxstring), which should have been +cmtxt(...,NULL) to defer the evaluation of the interior of the macro until it +was executed. This is better illustrated with the following example: + + { echo START, for \%i 1 3 1 { echo \%i }, echo STOP } + +which totally fell on its face prior to the fix. Also fixed ?-help for +immediate macros, which was broken too. ckuusr.c, 23 Aug 2002. + +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 (because RFC 959 and 1123 are +contradictory, plus no servers follow either one of them for this anyway). 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 (which happens +with ProFTPD). ckcftp.c, 25 Aug 2002. + +Added SET FTP ANONYMOUS-PASSWORD (plus help text and show value). ckcftp.c, +25 Aug 2002. + +Made FTP command "not available" if NOFTP is defined. ckuusr.c, 25 Aug 2002. + +Forced client to send a TYPE command upon initial connection, since given +the variable quality of FTP servers, it's not safe to assume the server is +in ASCII or any other particular mode. ckcftp.c, 25 Aug 2002. + +SET FTP CHARACTER-SET-TRANSLATION ON is completely broken in K95, although it +works fine in C-Kermit. Furthermore it is broken in both the GUI and Console +versions, so it's not a Unicode vs OEM console-character-set issue. + +Added Concurrent PowerMAX OS target from Tom Horsley. makefile, ckuver.h, +27 Aug 2002. + +Minor fixes to FTP module from Jeff. ckcftp.c, 27 Aug 2002. + +New Makefile target for Mac OS X 10.2, needs -DNDSYSERRLIST added, from +William Bader. 2 Sep 2002. + +SET OPT DIR /DOTFILES didn't work for server listings. A few years ago when +I front-ended zxpand() with nzxpand(), I missed a couple places where +traverse() needed to refer to xmatchdot (nzxpand's argument flag) rather than +global matchdot. Fixed in traverse(): ckufio.c, 2 Sep 2002. + +From Jeff, 4 Sep 2002: + . setautodl(x) -> setautodl(x,y): ckuusr.h, ckuus[7y].c + . Add another parameter to popup_readblah(): ckuus6.c + . Sort out some confusion in scanfile() where a parameter was also used as a + local flag. ckuusx.c. + . Protect restoring of saved terminal idle parameters with a flag that says + they were actually saved. ckuusr.c. + . Rework uq_text() and uq_mtxt(). ckuus3.c. + . Fix FTP charset translation for little-endian hardware: ckcftp.c. + +The latter still doesn't work in Linux: + + (/home/fdc/kermit/) C-Kermit>set ftp server-character-set latin1-iso + (/home/fdc/kermit/) C-Kermit>set file character-set utf8 + (/home/fdc/kermit/) C-Kermit>get latin1.txt + +Results in "????????: file not found". But it works fine on the Sun. + +Jeff's patch removed a little-endian byte-swap (LEBS) from doftpsend2(). But +the real problem was that LEBS was not being done consistently throughout the +module. There were similar xgnbyte()/xpnbyte() loops elsewhere in the code, +and all of them needed to work the same way. Undoing Jeff's fix and then +adding the LEBS to the loop in getreply() makes downloads work right, but the +messages are still messed up (they come out in Chinese :-) Begin by moving all +byte-swapping operations that occur in ckcftp.c itself into a new function, +bytswap(). It's either right to do it all the time, or to do it never; this +way we can turn it on and off in one place. + +xp/gnbyte() include behavior that depends on what Kermit is doing: W_SEND, +etc. xpnbyte() tests W_KERMIT, which is a combination of W_SEND, W_RECV, etc. +Defined a new symbol W_XFER, which is like W_KERMIT but includes W_FTP. These +are all the "whats" in which character sets might need to be converted. +Changed the W_KERMIT reference in xpnbyte() to W_XFER. Fixed the inderminate +"what" state after an FTP command by moving "what = W_COMMAND;" from before +the main parse loop to inside it (this didn't matter before the addition of +FTP but now it does). ckcker.h, ckcftp.c, ckuus5.c, 6 Sep 2002. + +Finally I changed xlatec() to be consistent with all the other xgnbyte() / +xpnbyte() usage throughout the FTP module and, poof, everything worked in +Linux (and still works on the Sun). We still need some work in Windows (where +the file character-set is not necessarily the console character set for +messages) but we can tackle that next. ckcftp.c, 6 Sep 2002. + +Checking yesterday's work: + +Kermit file transfers with charset translation work fine in both directions. + +FTP GET with charset translation works fine on both BE and LE + +Fixed a typo in yesterday's changes that made FTP PUT with charset translation +always upload 0-length files. ckcftp.c, 7 Sep 2002. + +FTP PUT (after the typo was fixed) with charset translation works fine on BE, +but on LE the message comes out in Chinese and the resulting file gets ? or +nothing for all for the accented letters: + + FTP... Kermit + Up Dn Up Dn Term + BE OK OK OK OK xx + LE no OK OK OK xx + +xx = C-Kermit CONNECT mode with translation doesn't seem to do anything, not +only in today's code, but also in the 8.0 release version: "set term char +latin1 utf8" -- SHOW CHAR shows the right stuff, but no translation is done. +Ditto for the 7.0 release. That can't be right... + +But one problem at a time -- what's wrong with LE FTP uploads? Note that +XLATE works on the same machine, so it's obviously confusion in xgnbyte() +about "what". Suppose we make xgnbyte() ALWAYS return bytes in BE order. +This makes sense because xgnbyte() is almost always used to feed xpnbyte(), +and xpnbyte() requires its bytes to come in BE order. This means that all +code that uses xgnbyte()/xpnbyte() loops can be simplifed, which I did for +the FTP module. ckcfns.c, ckcftp.c, 7 Sep 2002. + +Of course Kermit protocol uses xgnbyte() too, but only for filling +packets, and packets never contain UCS2 and even if they did, it would have +to be big-endian, so no changes needed for getpkt(). Now we have: + + FTP... Kermit + Up Dn Up Dn + BE OK OK OK OK + LE OK OK OK OK + +Now let's look at the remaining xgnbyte() calls in the rest of the code: + +ckuus4.c: + xlate() uses it of course. I simplified the general-case loop. + Works OK on both Sun and Linux. + +ckuus6.c: + typegetline() uses it. I commented out the byte swap. Seems OK. + +Built and tested on Linux, Solaris, and SunOS. I'm sure I must have broken +something, but the main things are better than they were. Kermit and FTP +transfers need testing in K95, as well as the TYPE command (there's a bunch of +special K95 code in there). C-Kermit charset translation during CONNECT is +still broken, or else I forgot how to use it, but that's a separate issue +since xgnbyte()/xpnbyte() are not involved. And we still need to do something +in FTP getreply() for K95 to convert messages to the console character set for +display, rather than the file character set (should be trivial). Also there's +still a lot of extra debugging and commented-out junk in ckcftp.c to be +cleaned up after more testing. + +During yesterday's testing, I noticed that REMOTE SET { FILE, XFER } +CHARACTER-SET didn't work. The server accepted these commands but they didn't +seem to do anything. In fact, they did work, but they were undone later by +code in sfile() that restored the global settings in case they had been +temporarily overridden by autoswitching or whatever. The solution is to +"unsave" the saved values whenever a global setting is performed explicitly. +Tested successfully against Sun and Linux servers. Also the server end of +REMOTE SET needed updating for Unicode. ckcfn[s3].c, ckuus3.c, 8 Sep 2002. + +Cleaned commented-out cruft and extra debugging from ckcftp.c. 8 Sep 2002. + +Kermit autodownload with ASK file dialog: if user supplied an absolute +pathname, it was treated like a relative one. Fixed the invocation of +uq_file() in rcvfil() to temporarily override the RECEIVE PATHNAMES setting. +ckcfns.c, 10 Sep 2002. + +Added SET TERMINAL ROLL KEYSTROKES { SEND, RESTORE-AND-SEND, IGNORE }, parse +only. Needs implementation (search for tt_rkeys and rollkeytab in ckuus7.c). +ckuusr.h, ckuus[27].c, 10 Sep 2002. + +If FILE INCOMPLETE is DISCARD and a file is being received by IKSD but IKSD +gets a Telnet LOGOUT command, the partial file is not deleted. In fact this +happens any time doexit() is called for any reason during file reception, +e.g. because of SIGHUP. Added code to doclean() to check if a download +output file was open, and if so, to delete it after closing it if keep==0. +ckuusx.c, 10 Sep 2002. + +Added a brief one-line message after remote-mode file transfer saying +what (or how many) file(s) were transferred, where they went, and whether +the transfer was successful -- kind of an automatic WHERE command, useful +with autodownloads so you know what happened. ckcpro.w, 11 Sep 2002. + +The Unix and VMS C-Kermit CONNECT modules have botched remote-charset to +local-UTF8 translation ever since the Unicode was first added in v7.0. Fixed +in ckucns.c, ckucon.c, ckvcon.c, 11 Sep 2002. + +On to pattern-matching... The matchdot business should apply only for (Unix) +filename matching, not for general string matching. Fixed in ckmatch(): +ckclib.c, 11 Sep 2002. + +A bigger problem occurs in filename matching. Somehow the dirsep == fence +business interferes with matching {foo,bar,baz} segments. For example, I have +a filename "foo" and I want to match it with the pattern "{foo,bar}". Somehow +the segment pattern becomes "*/foo" and doesn't match the string. Where does +the '/' get tacked on? I don't even know how to explain this, but the short +story was that ckmatch(), under certain circumstances, would back up to before +the beginning of the filename string, which just happened to contain a "/" +(and before that a ".") because of who was calling it. Obviously this is not +how to write a pattern matching function... Ensuring that it never backs up +beyond the beginning of a string fixed the immediate problem and does not seem +to have broken any other matching scenarios (I have 150 of them in my test +script). ckclib.c, 11 Sep 2002. + +There's still a problem though. Suppose the a client sends "dir {{.*,*}}" to +a server. This asks for a directory listing of all files that begin with +dot as well as all files. Still doesn't work because we don't normally show +dot-files, but in this case it SHOULD work because ".*" was explicitly +requested. Staring at the ckmatch() code revealed how to fix this, and I did, +but that was only half the problem. The other half was that the list of +files being fed to ckmatch() did not include the dotfiles in the first place. +The cure here is to change nzxpand() to prescan the pattern to see if it +includes a leading dot, and if so to set the "xmatchdot" flag itself, even +if it wasn't set by the caller. ckclib.c, ckufio.c, 11 Sep 2002. + +Now that {foo,bar,...} patterns work better, I added a quick hack to the +DIRECTORY command to allow multiple filespecs to be given, in which case we +combine them into a {file1,file2,...} pattern before calling nzxpand(). Works +fine but it's a hack because you don't get file lists upon "?" in the second +and subsequent filespec fields, but I doubt anyone will notice. So now, +finally, people can do "dir .* *" like they do in Unix (except with ls) to get +a listing of all files in a directory without having to know about or use the +/DOTFILES switch. This was NOT done for the server end of RDIR because of +ambiguity of spaces as separators versus filename characters.) domydir(): +ckuus6.c, ckuus[r2].c, 11 Sep 2002. + +Added a CONTINUE command. In a script, this does whatever CONTINUE did before +(e.g. in a FOR or WHILE loop). At the prompt, it calls popclvl(), which gives +a more natural way to continue a script that has "shelled out" to the prompt. +ckuusr.[ch], 11 Sep 2002. + +Added help text for CONTINUE. ckuus2.c, 12 Sep 2002. + +From Jeff, 16 Sep 2002: + . SET TERM ROLL KEYSTROKES for K95: ckuusr.h, ckuus7.c + . Remove the doexit() call from the Telnet TELOPT_LOGOUT handler: ckctel.c + +Fixed an FTP debug message to be consistent with Kermit ones. +ckcftp.c, 16 Sep 2002. + +Added SET/SHOW TRANSFER REPORT to turn the post-transfer report off and on. +ckuusr.h, ckuus[234].c, 16 Sep 2002. + +Fixed Solaris (and maybe some other SVORPOSIX builds) to find out their full +hostname rather than just short form (e.g. watsol.cc.columbia.edu rather than +just watsol). ckhost(): ckuusx.c, 16 Sep 2002. + +"cat somefile | kermit -Ts -" is supposed to send stdin in text mode, but +K95's file transfer display reports BINARY. Looked at C-Kermit code; it seems +fine. Looked at packet and debug logs; C-Kermit was indeed sending in text +mode and announcing it correctly. K95 gattr() is doing the right thing: + + gattr file type[AMJ]=3 + gattr attribute A=text=0 + gattr sets tcharset TC_TRANSP[A] + +Same thing happens when C-Kermit is receiving. Yet when I send an actual +file, rather than stdin, it's received in text mode. The only difference is +that stdin does not have a Length attribute in its A-packet, so in this case +the receiver skips any calls to screen() that show the length or percent done. +Aha, so maybe it's just a display problem -- scrft() is not being called to +repaint the file type if the size was not known. Fixed in opena() by +removing the IF clause from "if (fsize > -1L) xxscreen(SCR_FS,0,fsize,"");". +ckcfn3.c, 18 Sep 2002. + +K95 user has a listfile containing some regular filenames and then some +filenames that include paths and has all kinds of problems with MGET /LISTFILE +(pieces of different names concatenated to each other, etc). Setting up the +same scenario here, I don't see the same problems but I do see "Refused: Name" +when we go to get a path/name file. This happens because (a) we had already +got a top-level file with a certain name, (b) a file in a subdirectory has the +same name, (c) we are stripping the path before calling zchki(), and (d) +FTP COLLISION is set to DISCARD. How do we make FTP not strip the path? + +This is an interesting question... The answer depends on where the user +wants the file to go. Normally if you tell an FTP client to "get foo/bar", +you want the file "bar" to be downloaded to the current directory. + +Anyway, it turns out the FTP module uses paths locally during MGET only if +/RECURSIVE was specified. So: + + mget /listfile:blah /recursive + +should have made this work, but it didn't because in the /LISTFILE case, +we have effectively turned an MGET into a series of GETs, where the code to +check whether to strip the path didn't check the recursive flag because how +could a GET (as opposed to an MGET) be recursive? Adding this exception to +the if-condition got us a bit farther but now when we try to open the output +file in doftprecv2(), zopeno() fails because the name contains a dirsep. +We have to call zmkdir() first but that wasn't happening because some other +flag wasn't set right in this case. Finally zmkdir was called, but with +the wrong string. After fixing that, it works. Now we should be able +to use /RECURSIVE to force the pathname to be used on the local end. +ckcftp.c, 19 Sep 2002. + +Checked FTP filename conversion issues. FTP FILENAMES AUTO is supposed to +mean LITERAL if "wearealike" OR server is UNIX or Windows, otherwise +CONVERTED, but there were places where this rule was not applied consistently, +fixed now. ckcftp.c, 21 Sep 2002. + +Added SET FTP DISPLAY, which is like SET TRANSFER DISPLAY but applies only to +FTP, mainly because I tended to type it all the time. Now if you have dual +sessions, each session can have its own transfer display style. ckcftp.c, +ckuusr.h, ckuus[347].c, 21 Sep 2002. + +Back to FTP MLSD. We're supposed to match the pattern locally, not rely on +the server to filter its list according to the client's pattern. Thus we must +also allow an empty argument to MGET and must not send a filespec with MLSD. +Actually this is tricky -- how is the client supposed to know whether to send +a filespec. For example, if the user's command is "mget foo*bar", and the +server supports MLSD, then what should the client do? The client does not +know the wildcard syntax on the server, so for all the client knows, this +might be a valid directory name, in which case it should be sent. On the +other hand, the user might intend it as a wildcard, in which case it should +NOT be sent. But the FTP client can't read the user's mind. This is another +serious flaw in the Elz/Hethmon draft. Anyway, I got the local matching +business working for MLSD as long as the user's MGET arg is really a pattern +and not a directory name. To be continued... ckcftp.c, 21 Sep 2002. + +Added FTP { ENABLE, DISABLE } { FEAT, MLST }. If we always send FEAT, we +usually get a complaint. If we send FEAT and MLST is negotiated, there is a +good chance it is misimplemented or will have undesirable side effects, such +as sending huge file lists. NOTE: /NOINIT on the FTP OPEN command also +disables both of these. ckcftp.c, 22 Sep 2002. + +Fixed mkstemp() code in FTP remote_files(). mktemp() does not open the file, +mkstemp() does open it; previously we had been opening it again and never +closing the first instance so every MGET would create another open file +descriptor. ckcftp.c, 22 Sep 2002. + +Added debug messages for temp-file creation and lines read from the temp file. +ckcftp.c, 22 Sep 2002. + +Eliminated sending of some extraneous TYPE commands, but there's still room +for improvement. ckcftp.c, 22 Sep 2002. + +Moved definition of build date to top of ckcmai.c. 22 Sep 2002. + +Added recursion to MGET with MLSD... It's all done in remote_files(). +Temp-file pointers are on a stack (max size 128). When parsing MLSD lines +from the temp file and we see "type=dir", we create the local directory by +calling zmkdir(), change to the remote by sending CWD, increment the depth, +and call ourselves. When reading from a temp file, upon EOF we close and +dispose of the temp file, return -3 if currently at top level, otherwise we +free the tmpfile name, decrement the depth, send CDUP to the server, "cd .." +locally, and go back and read the next line from the previous but now current +temp file. Conceptually simple but needed hours of debugging -- what must +be static, what must be on the stack... Seems OK now but still needs some +heavy testing. ckcftp.c, 22 Sep 2002. + +Added FTP { ENABLE, DISABLE } { SIZE, MDTM } and add help text for FTP +ENABLE and DISABLE. ckcftp.c, 23 Sep 2002. + +Don't allow restart if SIZE disabled. ckcftp.c, 23 Sep 2002. + +Make sure all implicit SIZE commands are surpressed if SIZE disabled. +ckcftp.c, 23 Sep 2002. + +If an explicit FTP MODTIME command is sent when MDTM is DISABLED, and it +succeeds, re-ENABLE MDTM. Ditto for SIZE. ckcftp.c, 23 Sep 2002. + +If an explicit FTP FEATURES command is sent during an FTP session, redo the +features database from it. ckcftp.c, 23 Sep 2002. + +After further discussion with Robert Elz, I realized I had to expose the +underlying MGET mechanisms to the user; the draft isn't going to change, and +the new spec will result in undesirable effects if the client tries to "do the +right thing" by magic in all situations; thus the user must have some new +controls: + + MGET [ /MLST, /NLST, /MATCH:xxx ] [ filespec [ filespec [ ... ] ] ] + +These switches let the user force the use of MLSD or NLST when there's a +choice, and to force local use of a pattern rather than sending it to the +server, and even to send a directory name to the server at the same time as +specifying a pattern for local matching, and of course by default we try to do +the right thing in all scenarios. Symbols only; not coded yet. ckuusr.h, +23 Sep 2002. + +Added the three new switches to MGET, plus /MLST is an invisible synonym for +/MLSD. If /NLST or /MLSD is given it, it forces the corresponding FTP protocol +directive. ckcftp.c, 25 Sep 2002. + +Now for the tricky part: now we have two separate concepts for what to send to +the server: a filename or wildcard to be interpreted by the server (NLST only) +or a directory from which to get a list of all the files (NLST or MLSD), +possibly together with a pattern to be used by the client to match filenames +returned by the server. This required giving remote_files() an additional +argument. Now it uses "pattern" (if any) strictly for local pattern matching +(because now it is the /MATCH: switch argument, not the MGET filespec), and +"arg" (the MGET filespec) is what it sends to the server, maybe (see the +comments in the code for the actual details); either or both these can be +null. ckcftp.c, 25 Sep 2002. + +Discovered that "mget foo", where foo is a directory name, never worked. +Fixed in remote_files(): ckcftp.c, 25 Sep 2002. + +Going through every combination of NLST, MLSD, /MATCH:, and MGET arg and +debugging each case until OK... Then also with the panix.com NetBSD server +(lukemftpd 1.0) which also supports MLSD.... 11 test cases all debugged and +tested OK. ckcftp.c, 26 Sep 2002. + +Added /NODOTFILES switch to FTP MGET, to control what happens with dot-files +if the server includes their names in the list (as lukemftpd does). There's +no point in adding a /DOTFILES switch because what could it possibly do? +ckcftp.c, 26 Sep 2002. + +Changed a bunch of "skipthis++" to "continue" in doftpget(), to avoid +error messages when skipping files that user said she wanted to skip. +ckcftp.c, 26 Sep 2002. + +Added help text for the new MGET switches. ckcftp.c, 26 Sep 2002. + +Don't switch LOCUS when making an FTP connection until logged in. +ckcftp.c, 26 Sep 2002. + +Fixed LDIR to run Kermit's built-in DIRECTORY code rather than the external +directory program. ckuusr.c, 26 Sep 2002. + +Protect iswild() against NULL args. ckufio.c, 26 Sep 2002. + +From Jeff: SET GUI WINDOW RUN-MODE { MAXIMIZE, MINIMIZE, RESTORE }, +plus variables for GUI Window X position, GUI Window Y position, GUI +Window X resolution, GUI Window Y resolution, GUI Window Run mode. +ckuusr.h, ckuus[24].c, 27 Sep 2002. + +From Ronan Flood: updated FreeBSD 1.0 makefile entry, plus an #ifdef to protect +sysconf() calls. makefile, ckutio.c, 28 Sep 2002. + +Change ftp_auth() to return(0) if an AUTH command gets a 500 response, so it +doesn't keep sending other kinds of AUTH commands. ckcftp.c, 29 Sep 2002. + +Changes from Jeff to yesterday's changes. ckcftp.c, 30 Sep 2002. + +From Jeff: SSH command-line personality. Uses same command line as the Telnet +personality. ckcker.h, ckcmai.c, ckuus[4y].c, 3 Oct 2002. + +From Jeff, 7 Oct 2002: + . SET PRINTER CHARACTER-SET. ckuxla.c, ckuusr.h, ckuus[25].c + . Promotion of K95 to Beta.01. ckcmai.c + . Promotion of SET GUI { MENUBAR, TOOLBAR } to visible. ckuus3.c + +Changed the URL parser as follows: if the username and/or password fields are +present but empty, as in: + + ftp://@ftp.xyzcorp.com/somepath + or: ftp://:@ftp.xyzcorp.com/somepath + but not: ftp://:ftp.xyzcorp.com/somepath + +the pointer for these items becomes a pointer to an empty string, rather than +a NULL pointer. Then when we go to open the connection, if the username +string pointer points to an empty string, we prompt for the username (and/or +password). ckuusy.c 9 Oct 2002. + +Jason Heskett reported an interesting bug involving a core dump when an +ON_EXIT macro is defined that executes another macro. Sometimes. He was able +to send a short command file that always crashed. Diagnosis: ON_EXIT, when it +is called, pokes itself out of the macro table by setting its own entry in the +macro name list to an empty string. But this interferes with any macro +lookups that are done while executing ON_EXIT's body and also evidently some +code is not happy with empty macro names... To fix: replace "on_exit" with +"on_exxx", so the replacement keyword is (a) nonempty, and (b) doesn't wreck +the alphabetical sorting of the table. ckuusx.c, 9 Oct 2002. + +Added makefile targets for FreeBSD 4.6 and 5.0. Built and tested on 4.6; +don't know about 5.0. ckuver.h, makefile, 9 Oct 2002. + +Added targets for AIX 5.2 and 5.3; totally untested. ckuver.h, makefile, +9 Oct 2002. + +Built current source on Unixware 7.1.3 (make uw7); it's fine. 9 Oct 2002. + +Promoted C-Kermit to 8.0.206 Beta.01 in hopes of a simultaneous release +with K95 2.1. ckcmai.c, 9 Oct 2002. + +From Jeff: Change KERMITFONT definitions to use the new (Unicode 3.1) code +points for the terminal graphics characters (such as VT100 horizontal scan +lines), rather than private-use codes. ckcuni.c, 10 Oct 2002. + +Jason Heskett also complained that REMOTE CD would print the name of the new +directory returned by the server even if he SET QUIET ON. This is a tricky +one. Which server replies should the QUIET settings apply to? If I give a +REMOTE DIRECTORY command, it means I want to see the directory listing, +period. But if I give a REMOTE CD command, I get an "unsolicited" response +message that SET QUIET ON should suppress. Adding message suppression to +rcv_shortreply() is close, but not totally right; for example, it also +suppresses the response to REMOTE PWD, which is not helpful. The only right +way to do this is to suppress for REMOTE CD only, which can be done only by +setting a (new) global flag, rcdactive. ckuus[r57].c, ckcpro.w, 10 Oct 2002. + +Ditto for REMOTE LOGIN response message ("Logged in"). ckuus7.c, 11 Oct 2002. + +From Jeff: SET GUI WINDOW FONT { NAME, SIZE }. ckuusr.h, ckuus4.c, 11 Oct 2002. + +Quick preliminary 8.0.206 build-all: + + OK SunOS 4.1.3 + OK Solaris 2.5.1 + OK Solaris 9 + OK AIX 4.3.3 + OK HP-UX 10.20 + OK VMS 7.1 Alpha + TCP/IP + OK VMS 7.1 Alpha nonet + OK VMS 5.5 VAX + TCP/IP + OK VMS 5.5 VAX nonet + OK Unixware 7.1.3 + OK FreeBSD 3.1 + OK FreeBSD 4.6 + OK NetBSD 1.5.2 MVME (Gerry B) + OK Sinix 5.42 + +Sinix build got stuck on ckuusr.c even though we're not optimizing on Sinix +any more. Rebooting the machine fixed it. + +Fixed some #ifdefs for VMS in new incomplete-file deletion code in doclean(). +ckuusx.c, 11 Oct 2002. + +Moved uq_blah() prototypes from ckuusr.h to ckcker.h because these routines +are called in modules that don't (and shouldn't have to) include ckuusr.h. +11 Oct 2002. + +Jeff verified secure builds on Linux and Solaris. + +Custom-build workout: 80 different feature-selection combinations: + . Fixed yesterday's change for NOSPL: ckcfns.c. + . Fixed conflict between NORECALL and USE_ARROWKEYS: ckucmd.c. + . Moved setseslog() from ckuus5.c to ckuusx.c to avoid link-time foulups. + . Fixed an unguarded reference to zmkdir() in ckcftp.c. + . Protected rmsg() by #ifndef NOXFER: ckuus7.c. + . Protected initxlist() by #ifndef NOXFER: ckcmai.c. + . Fixed unguarded references to g_url struct in xx_ftp(): ckuusy.c. + . Fixed unguarded references to tt_snaws() in winchh(): ckutio.c. + +--- 8.0.206 Beta.01 11 Oct 2002 --- + +From Jeff, 16 Oct 2002: + . Fix K95 RMDIR: ckcfn3.c. + . Makefile targets for Red Hat 7.2, 7.3, 8.0: ckuver.h, makefile. + . Added \v(log_xxx) for each kind of log for PeterE: ckuusr.h, ckuus4.c. + . Added SET TERM ATTRIBUTE DIM { ON, OFF }: ckuus[27].c. + . Change "const" to "CONST" in some PAM declarations. ckufio.c. + +Added SET MATCH { DOTFILE, FIFO } { ON, OFF }. A FIFO special file is a named +pipe, used for interprocess communication. It must be opened at both ends, so +it's silly to match them by default; opening a FIFO and attempting to read +will block forever unless somebody is writing into the other end. Made the +MATCH FIFO default OFF in all cases. The dotfile default is the same as +always (OFF for UNIX, ON elsewhere); SET MATCH DOTFILE is simply a more +untuitive and findable command than SET WILD KERMIT /MATCH-DOT-FILES. Note +that SET MATCH DOTFILE undoes SET OPTIONS DIRECTORY /[NO]DOTFILES, and vice +versa. ckcmai.c, ckuusr.h, ckuus[23].c, ckufio.c. 17 Oct 2002. + +Added client and server end of REMOTE SET MATCH { DOTFILE, FIFO } { ON, OFF }. +The new protocol codes are 330 and 331, respectively. ckuus[367].c, ckcfns.c, +17 Oct 2002. + +Adjusted the "match dot if pattern starts with dot" heuristic in nzxpand() +to not kick in if the filespec is "./foo". This probably needs more work. +ckufio.c, 17 Oct 2002. + +Fixed typo in transcribing Jeff's ckcfn3.c code from yesterday. 18 Oct 2002. + +Moved some help text out of #ifdef ANYSSH that had nothing to do with SSH. +(Idea for a new EMACS feature: M-X list-ifdef-environment.) +ckuus2.c, 18 Oct 2002. + +Removed "set file { permission, protection }" keywords, which led nowhere. +ckuus7.c, 18 Oct 2002. + +Added -DSV68 to all SV/68 targets. Make ckgetfqhostname() just return its +argument in SV/68; it dumps core otherwise. In case this happens anywhere +else, add -DNOCKGETFQHOST to CFLAGS. makefile, ckcnet.c, 18 Oct 2002. + +For PeterE, added SET { SEND, RECEIVE } PERMISSIONS { ON, OFF } so incoming and +outbound permission attributes can be set separately. ckuus[27].c, 18 Oct 2002. + +Changed SHOW ATTRIBUTES to show In and Out permissions separately. +ckuus5.c, 18 Oct 2002. + +Fixed REDO to display the command it's redoing and to add it to the bottom +of the recall buffer. ckuusr.c, 18 Oct 2002. + +Discovered that DATE SATURDAY dumps core... Apparently it always did; this +case was not included in the date-time torture test script. The crash happens +because the DATE parsing code doesn't check for a NULL date-converion +error-message pointer. Fixed in ckuusr.c, 18 Oct 2002. + +The reason DATE SATURDAY got a date-conversion error was that this path thru +the code left a result pointer unset. Fixed in cmcvtdate(): ckucmd.c, +19 Oct 2002. + +DATE SUNDAY +1DAY returned incorrect results (for any day-of-week name, any +delta time), even though DATE TODAY +1DAY worked fine. Fixed in cmcvtdate(): +ckucmd.c, 19 Oct 2002. + +SET TAKE ECHO ON counted each line twice when GOTO was active. Fixed in +dogoto(): ckuus6.c, 19 Oct 2002. + +Jeff noticed: +"KERMIT READY TO GET... + RCVD: (2 files) Last: [/amd/prost/p/kd/jaltman/.src/ckonet.c] (OK) +the last file attempted may have been ckonet.c but it certainly was +not the last file received" (similarly for sending). Fixed by having two +pointers for each name; a preliminary pointer, which is set for each file at +the beginning of the transfer (when we have all the needed info), and a final +one that is set from the preliminary one only after the file was transferred +successfully. This corrects not only the automatic "wheremessage" at the end +of a remote-mode transfer, but also the WHERE and SHOW FILE command results. +ckuusx.c, ckcfn[s3].c, ckcpro.w, 19 Oct 2002. + +From Jeff: Improve ORIENTATION message for K95 to say which directories are +for which INI files. ckuusr.c, 23 Oct 2002. + +Removed Beta designation from herald. ckcmai.c, 23 Oct 2002. + +Put final dates and ID strings in Unix and VMS build procedures. +Makefile, ckvker.com, 23 Oct 2002. + +Build-all... #ifdef adjustments: ckcfns.c... 83 different feature-set +combinations build OK on Linux. 23 Oct 2002. + +From Jeff: SET WIN95 HORIZONTAL-SCAN-LINE-SUBSTITUTIONS. ckuusr.h, ckuus7.c, +24 Oct 2002. + +Fixed Heath-19 graphic character-set table to use new Unicode 3.1 values +if WIN95 HORIZ OFF. ckcuni.c, 24 Oct 2002. + +Changed tx_usub() to return Unicode 3.1 values if WIN95 HORIZ OFF. +ckcuni.c, 24 Oct 2002. <-- No backed off on this. + +Some problems during build-all: + + . VMS 7.1 TGV 4.2: If I make a Telnet connection with it, then try to send + a file (itself. wermit.exe) over the connection, the connection drops + after about 20%, the thermometer zooms out to 100% and SUCCESS is reported. + This doesn't happen with UCX. + + . VMS 7.3 TGV 4.3: ckcmai.c won't compile because of a complaint about the + declaration of select() (which ckcmai.c doesn't use) in + SYS$COMMON:[SYSLIB]DECC$RTLDEF.TLB. Ditto in VMS 7.2 TGV 4.3. + Adding NOSELECT to CFLAGS doesn't help. I don't think the VMS version + even uses select(). But the TGV 4.3 builds were OK in 8.0.201, so what + changed? I don't see anything in ckcnet.h that would have done it. + +It builds OK with VMS 7.1 / TGV 4.2 but sending files on Telnet connections +fails in a strange way: the connection drops, but the thermomoter goes to 100% +and success is reported. I don't know why the connection is dropping (s_errno +is 32 == "broken pipe"), but the spurious success indication is because of +a double failure in sdata(): (1) The packet-sending loop index could go +negative without breaking the loop when streaming; (2) if spack() fails, +sdata() should return -2, not -1 (which means EOF). Also if any ttchk() in +sdata() returns < 0, sdata should return -2. I fixed this code (which has +been this way for YEARS) and now VMS C-Kermit properly fails when it gets +the spack() error, but ttchk() in this case still doesn't think the connection +is lost, but that must be our new problem with MultiNet. ckcfns.c, +27 Oct 2002. + +The compilation failure in ckcmai.c is a clue... The problem was that I added +#ifdef VMS / #include / #endif to shut up complaints about the time() +call. Evidently something in VMS gives MultiNet a bad case of +indigestion; removing it fixes the compilation and the result works fine. The +transmission failures in the other case seem to be a coincidence -- something +to do with the U of Arizona (probably some obscure VMS quota on my IDs there, +or some kind of network connection throttling), since it doesn't happen +anywhere else. ckcmai.c, 27 Oct 2002. + +Changed four occurrences of "void" to "VOID" in ckcftp.c, 27 Oct 2002. + +Defined NOCKFQHOSTNAME for HPUXPRE65. Might also need this for HP-UX 7 +and maybe 8. ckcnet.c, 27 Oct 2002. + +From Jeff: PAM_CONST definition to clear up warnings caused by different +vendors' definitions of PAM structs. ckufio.c, 28 Oct 2002. + +Unixware 2.1.0 build bombs immediately with "UX:make: ERROR: line too long" +(which line?) Not worth chopping up the makefile to fix but in a pinch it +could be done. 2.1.3 builds OK. + +Did another 20-some platform builds, bringing the total to 83, plus a final +runthrough of the build-with-84-different-feature-set-combinations script on +Linux. Should be good to go! + +--- 8.0.206 24 Oct 2002 --- + +Finally got access to Linux on IA64 again. Builds OK but dumps core +immediately on startup. Adding -DNOCKGETFQHOST was necessary but not +sufficient. In this case, the very call to ckgetfqhostname() from ckhost() +(which is in ckuusx.c) was dumping core; thus I had to move the definition of +NOCKGETFQHOST from ckcnet.c to ckcdeb.h, add an #ifdef __ia64__ clause to it, +and protect the call to ckgetfqhostname() with it. Obviously there has to be +a better fix but this will have to for now. ckcnet.c, ckuusx.c, ckcdeb.h, +31 Oct 2002. + +Link step fails in Mandrake 9.0 with undefined references to res_search, +dn_expand, and crypt. Turns out the linux makefile target tests for the +existence of libcrypt.a and libresolv.a, but in Mandrake 9.0 they exist +only as *.so. Changed linux target to look for both. makefile, 1 Nov 2002. + +Vace reported that "ftp mget a b c" would get ALL files from the server's +directory if c did not exist. Diagnosis: off-by-one error in counting MGET +args processed. Naturally fixing this bug revealed another (this time +cosmetic) one, which resulted in spurious error messages when hitting MGET +args that have no match on the server. Fixed in ckcftp.c, 1 Nov 2002. + +Rebuilt about 60 of the most important Unix binaries to pick up the fixes +31 Oct - 1 Nov 2002, and refreshed the FTP site with new sources, tarballs, +ZIP files, etc. Sat Nov 2 19:11:30 2002 + +From Martin Vorlaender and Jeff, SSL/TLS support for VMS: +ckuusr.h, ckuath.h, ckcnet.c, ckctel.c, ckuath.c, 10 Nov 2002. + +Added PASV as invisible synonym for PASSIVE. ckcftp.c, 10 Nov 2002. + +--- 8.0.206 24 Oct 2002 #2 --- + +More work on SSL in VMS: Jeff + Martin Vorlaender: ck_ssl.c ckcmai.c ckcnet.c +ckctel.c ckuath.h ckvcon.c ckvtio.c ckvker.com 10-15 Nov 2002. + +Discovered that ckvfio.c would not compile on VMS 6.1 with UCX 4.1 because + was missing, which is explicitly included. Enclosed the +#include in #ifdef NOCONVROUTINES..#endif and rebuilt with +@[users.fdc.src]ckvker.com "" "" "NOCONVROUTINES". 16 Nov 2002. + +Fixed the "ftp mget */data/*" problem with two small changes to doftpget(): +ckcftp.c, 16 Nov 2002. Placed a copy of ckcftp.patch in ~kermit/f. + +From Lucas Hart: Fixes for VAX C 2.x and CMU TCP/IP. "Can't guarantee that +the revised CKVOLD will work for all combinations of more recent +VMS/compiler/TCPIP releases, but I've tested it for compatibility on our AXP +VMS 6.2, UCX 4.0, DECC 5.6, your AXP VMS 7.1, DEC TCPIP 5.1, DECC 6.0 as well +as with VAX VMS 5.4, VAX C 3.2, CMU12 and VAX VMS 4.7, VAX C 2.4." ckvfio.c, +ckvtio.c, ckuus5.c, ckvker.com, ckvold.com, 17 Nov 2002. + +From Jeff: More work on VMS SSL. Now it actually works, even in CONNECT mode, +except it hangs when it gets an error alert or the remote closes the +connection. ckcnet.c. ckvtio.c, 17 Nov 2002. + +NOTE: Lucas's changes should go into the 8.0.206 source code but it's too +late since ckvtio.c (upon which he based his changes) is already full of +SSL code. + +MGET in K95 in totally broken FOR SOME PEOPLE (mainly me) if the TMP (or TEMP) +value is too long. It works fine if you set these to C:\TMP. Diagnosis: (1) +we were malloc'ing only 16 bytes for the temp file name (I think this was my +fault -- I was only looking at the "ckXXXXXX" part and forgetting that this +was appended to the TMP path); (2) the Windows version of mktemp() wants you +to use the value pointed to by the pointer it returns, rather than assuming +the mktemp() arg will be modified in place. The code was changed to malloc a +longer string and to use the return value from mktemp() (if any) rather than +assuming that mktemp() modified its argument string (in K95 only -- not sure +about Unix platforms; the man pages differ on this, but at least this way if +some mktemp() version does NOT alter its argument, we still have a usable +filename (like /tmp/ckXXXXXX)). Of course this won't be good for recursive +MLSD downloads, but we can always issue patches for the Unix version later if +needed. The Linux and BSD versions use mkstemp() anyway. There is, however, +a danger of a memory leak in the Unix version if the user has defined a TMPDIR +or CK_TMP environment variable whose value is longer than 9 bytes. All this +is highly unlikely so we should be OK. ckcftp.c, 17 Nov 2002. + +--- K95 2.1.1 and updated ck[uv]206.{tar,zip} but not C-Kermit binaries --- + +From Jeff: Fixes for Telnet Com Port Control, update to a newer release of +OpenSSL: ck_ssl.c ck_ssl.h ckcdeb.h ckcftp.c ckcmai.c ckcnet.c ckctel.c +ckuath.c ckuath.h ckucns.c ckuus4.c ckuus5.c ckuusr.c ckuusr.h ckvcon.c +ckvfio.c ckvker.com ckvtio.c ckvvms.h, 25 Nov 2002. + +--- K95 2.1.2 and C-Kermit 8.0 CDROM --- + +From Jeff, 28 Nov 2002: + . Updated SSL modules: ck_ssl.[ch]. + . Fixed cipher-list display in SHOW AUTH & FTP ssl_auth(): ckuus7.c. ckcftp.c + . Some minor tn_wait() fixes: ckctel.c. + . Preliminary SSL support for VMS CONNECT: ckvcon.c. + +Bumped C-Kermit edit number to 207. + +From Jeff, 29 Nov 2002: "[C-Kermit was dumping core on SCO OSR5 Telnet Com Port +connections because] the SCO compiler treats all characters as signed. This +was causing 'sprintf(buf,"02x ",ch);' to produce strings such as "ffffffc2" +instead of "c2" for values between 128 and 255. This wrote beyond the end of +a buffer and blew away the stack. Having fixed this I also noticed that +conect() did not properly check for carrier when TN CPC was negotiated. This +has now been fixed as well." ckucns.c, ckucon.c, ckctel.c, 29 Nov 2002. + +From Jeff, 30 Nov 2002: Fix SSL for VMS and also carry forward the CPC fixes +to VMS. ckcnet.c, ckvtio.c, ckvcon.c, 30 Nov 2002. + +Changed copyright dates that are displayed (but not yet all the internal +ones) from 2002 to 2003. ckcmai.c, 3 Jan 2003. + +Fixed the FTP module's brief-format transaction log, which had the status +inverted: OK for FAILED and v.v. ckcftp.c 3 Jan 2003. + +From Jeff, 4 Jan 2003: + . Make /MOVE-TO:xxx convert xxx to full pathname: ckuus[r67].c, + . Make SHOW OPTIONS ALL show both kinds of options: ckuus2.c. + . More command-line personalities: ckcmai.c, ckuusy.c. + . New NOSCROLL command for K95: ckuusr.[ch], ckuus2.c. + . New lockdown and other command-line options: ckuusr.h, ckuusy.c. + . SSL interface updated to OpenSSL 0.9.7: ck_ssl.c. + . SET TERM LINE-SPACING and CURSOR xxx NOBLINK: ckuus[27]c. + . Expanded SHOW GUI command: ckuus3.c + . New SHOW TABS code: ckuus5.c. + +Updated SUPPORT (BUG), NEWS, and INTRO texts. ckuus[26].c. 5 Jan 2003. + +Fixed FTP module to suppress "'FEAT': Command not understood" message +unless FTP DEBUG is ON. ckcftp.c, 6 Jan 2003. + +Got a report that C-Kermit dumps core on Solaris when executing a certain +script. Seems to be related to changing vnambuf[] in zzstring() from an +automatic array to a malloc'd buffer (see notes from 29 Jun 2000). Changed +it to an automatic buffer except for K95. ckuus4.c, 6 Jan 2003. + +Nope, that's not it. It evidently happens only after FTP PUT has been used. +Fixed solaris9g makefile target to include -funsigned-char and built a new +binary. Determined that building with gcc and -funsigned-char makes no +difference. makefile, 7 Jan 2003. + +I did a preliminary audit, looking at the items in the left column: if used in +a given routine, are there any obvious mistakes: + + 1 2 3 4 5 + doftpput->putfile->sendrequest->doftpsend2->secure_write +malloc OK OK OK OK OK +makestr OK OK OK OK OK +automatic arrays OK OK OK OK OK +[ck]str[n]cpy OK OK OK OK OK +[ck]str[n]cat OK OK OK OK OK +sprintf OK OK OK OK OK +nzltor OK OK OK OK OK +zfnqfp OK OK OK OK OK +memcpy OK OK OK OK OK +bcopy OK OK OK OK OK + +secure_write sends the data directly on clear-text connections. On secure +connections, it calls secure_putbuf(), which calls secure_putbyte(), but we +aren't using those, so secure_write() is the end of the call chain for FTP +PUT. doftpsend2 has buf[] as an automatic array, which it reads file data +into using zxin (binary mode only), but this looks OK. Still, I changed it +read 1 less than the buffer size (fread) just in case. Also there was one +debug() statement that referred to an automatic array (fullname[]) before it +was initialized (but not used in this case), which I fixed. ckcftp.c, +7 Jan 2003. + +FTP GET /RECURSIVE somepath/somefile still didn't work, despite what the +notes of 19 Sep 2001 say. There are so many paths through the code, +depending on switch values, GET vs MGET, etc, that a crucial spot was missed. +Fixed in doftpget(): ckcftp.c, 7 Jan 2003. + +Back to the core dump... after two days of full-time debugging, I found the +culprit: the buffer-full test in the zzout() macro should have been ">=" +rather than just ">", thus Kermit wrote 1 byte past the end of the malloc'd +FTP PUT output buffer, ucbuf. Why did it never happen in K95? Because, since +it's a secure build, FUDGE_FACTOR is defined there. But it's not defined in +Solaris or other clear-text builds. Although the crash wouldn't happen in +secure builds, the 1-byte leak might have caused errors in the data transfer. +In non-Solaris clear-text builds, like Linux, I suspect that malloc() tends +add something for safety (especially given the man page statement that it +allocates "at least" what you asked for). Another reason the problem escaped +notice is that zzout() is used only for text-mode PUTs (and then only when +there is no character-set translation), but most transfers these days are +binary and/or downloads. Anyway, in the course of debugging, a lot of small +cleanups were done: sizeof(blah) for all arrays was replaced by the same +symbolic size that was used to allocate the array, numeric array sizes were +replaced with symbolic ones, etc. The real fix is one character long. +ckcftp.c, 9 Jan 2003. + +Got a report that "mget /recursive */somedir/*" downloaded the files into +the current directory, rather than re-creating the remote directory structure. +Fixed in doftpget(): ckcftp.c, 10 Jan 2003. + +Unix C-Kermit did not allow file transfer if started under inetd and accessed +via Internet raw socket (or whatever). Diagnosis: isatty() and friends would +fail causing ttopen() to fail. Fixed by adding escape clauses for "-l 0" +situations (i.e. Kermit invoked with an already-open file descriptor) at the +appropriate places. ckcmai.c, ckutio.c, 14 Jan 2003. + +From Jeff for K95 2.1.3 + . Add test for startflags & 128 to trap() for ignoring BREAK. + . Fix for SHOW TRANSMIT. + +--- K95 2.1.3 --- + +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 +signicant: + + . 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 doubleqote 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-Kerit 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. + +---8.0.211--- + +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 a 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. + +*********************** diff --git a/ckcasc.h b/ckcasc.h new file mode 100644 index 0000000..c7c32cc --- /dev/null +++ b/ckcasc.h @@ -0,0 +1,69 @@ +/* + 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, 2004, + 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 EM 25 /* Ctrl-Y */ +#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 */ diff --git a/ckcbwr.txt b/ckcbwr.txt new file mode 100644 index 0000000..5e70e65 --- /dev/null +++ b/ckcbwr.txt @@ -0,0 +1,1467 @@ + + C-Kermit 8.0 General Hints and Tips + + Frank da Cruz + [1]The Kermit Project, [2]Columbia University + + As of: C-Kermit 8.0.211, 17 March 2003 + This page last updated: Sat Apr 10 16:37:37 2004 (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: + + [3]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: + + [4]http://www.columbia.edu/kermit/ckubwr.html + + for Unix. This document also applies to [5]Kermit 95 for Windows, + which is based on C-Kermit. + + [ [6]C-Kermit ] [ [7]TUTORIAL ] + ________________________________________________________________________ + + CONTENTS + + 0. [8]PATCHES + 1. [9]INCOMPATIBLE CHANGES + 2. [10]THE C-KERMIT COMMAND PARSER + 3. [11]MULTIPLE SESSIONS + 4. [12]NETWORK CONNECTIONS + 5. [13]MODEMS AND DIALING + 6. [14]DIALING HINTS AND TIPS + 7. [15]TERMINAL SERVERS + 8. [16]TERMINAL EMULATION + 9. [17]KEY MAPPING + 10. [18]FILE TRANSFER + 11. [19]SCRIPT PROGRAMMING + ________________________________________________________________________ + + 0. PATCHES + + [ [20]Top ] [ [21]Contents ] [ [22]Next ] + + Source-level patches for C-Kermit 8.0.211: + + (None) + ________________________________________________________________________ + + 1. INCOMPATIBLE CHANGES + + [ [23]Top ] [ [24]Contents ] [ [25]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 [26]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, + [27]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 [28]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 [29]Section 5 of the + [30]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 [31]HTTP OPEN command. + ________________________________________________________________________ + + 2. THE C-KERMIT COMMAND PARSER + + [ [32]Top ] [ [33]Contents ] [ [34]Next ] [ [35]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 + + [ [36]Top ] [ [37]Contents ] [ [38]Next ] [ [39]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 + + [ [40]Top ] [ [41]Contents ] [ [42]Next ] [ [43]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 [44]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". + ________________________________________________________________________ + + 5. MODEMS AND DIALING + + [ [45]Top ] [ [46]Contents ] [ [47]Next ] [ [48]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 its repertoire of + commands and responses. Each modem make and model is likely to have a + different repertoire. Since Kermit has no way of knowhing 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 dailed 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 tradiational 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 + + [ [49]Top ] [ [50]Contents ] [ [51]Next ] [ [52]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 [53]Using C-Kermit (2nd Ed) for modem-dialing + troubleshooting instructions. The following sections offer some + addtional 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 [54]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 + + [ [55]Top ] [ [56]Contents ] [ [57]Next ] [ [58]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.0 + 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-originiated 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 + [59]RFC-2217 Telnet Com-Port Control protocol, these commands do + indeed take effect at the server's serial port. + ________________________________________________________________________ + + 8. TERMINAL EMULATION + + [ [60]Top ] [ [61]Contents ] [ [62]Next ] [ [63]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 semitrantsparent 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 + + [ [64]Top ] [ [65]Contents ] [ [66]Next ] [ [67]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 [68]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 + + [ [69]Top ] [ [70]Contents ] [ [71]Next ] [ [72]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 [73]Using C-Kermit. The following + sections touch on some that aren't. + + The [74]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 the HP-48, see: + + [75]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: + + * [76]Coping with Faulty Kermit Implementations (C-Kermit 7.0 and + later). + * [77]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 + + [ [78]Top ] [ [79]Contents ] [ [80]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 cancelled". + + 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. Other... + + Escape sequences (or any strings that contain control characters) + can't be used as labels, GOTO targets, or SWITCH cases. + + [ [81]Top ] [ [82]Contents ] [ [83]C-Kermit Home ] [ [84]C-Kermit 8.0 + Overview ] [ [85]Kermit Home ] + _________________________________________________________________ + + C-Kermit 8.0 Unix Hints and Tips / [86]The Kermit Project / + [87]Columbia University / [88]kermit@columbia.edu / 10 April 2004 + +References + + 1. http://www.columbia.edu/kermit/ + 2. http://www.columbia.edu/ + 3. http://www.columbia.edu/kermit/ckcbwr.html + 4. http://www.columbia.edu/kermit/ckubwr.html + 5. http://www.columbia.edu/kermit/k95.html + 6. http://www.columbia.edu/kermit/ckermit.html + 7. http://www.columbia.edu/kermit/ckututor.html + 8. http://www.columbia.edu/kermit/ckcbwr.html#x0 + 9. http://www.columbia.edu/kermit/ckcbwr.html#x1 + 10. http://www.columbia.edu/kermit/ckcbwr.html#x2 + 11. http://www.columbia.edu/kermit/ckcbwr.html#x3 + 12. http://www.columbia.edu/kermit/ckcbwr.html#x4 + 13. http://www.columbia.edu/kermit/ckcbwr.html#x5 + 14. http://www.columbia.edu/kermit/ckcbwr.html#x6 + 15. http://www.columbia.edu/kermit/ckcbwr.html#x7 + 16. http://www.columbia.edu/kermit/ckcbwr.html#x8 + 17. http://www.columbia.edu/kermit/ckcbwr.html#x9 + 18. http://www.columbia.edu/kermit/ckcbwr.html#x10 + 19. http://www.columbia.edu/kermit/ckcbwr.html#x11 + 20. http://www.columbia.edu/kermit/ckcbwr.html#top + 21. http://www.columbia.edu/kermit/ckcbwr.html#contents + 22. http://www.columbia.edu/kermit/ckcbwr.html#x2 + 23. http://www.columbia.edu/kermit/ckcbwr.html#top + 24. http://www.columbia.edu/kermit/ckcbwr.html#contents + 25. http://www.columbia.edu/kermit/ckcbwr.html#x2 + 26. http://www.columbia.edu/kermit/ck60manual.html + 27. http://www.columbia.edu/kermit/ckermit2.html + 28. http://www.columbia.edu/kermit/ck60manual.html + 29. http://www.columbia.edu/kermit/ckermit80.html#x5 + 30. http://www.columbia.edu/kermit/ckermit80.html + 31. http://www.columbia.edu/kermit/ckermit80.html#x2.2 + 32. http://www.columbia.edu/kermit/ckcbwr.html#top + 33. http://www.columbia.edu/kermit/ckcbwr.html#contents + 34. http://www.columbia.edu/kermit/ckcbwr.html#x3 + 35. http://www.columbia.edu/kermit/ckcbwr.html#x1 + 36. http://www.columbia.edu/kermit/ckcbwr.html#top + 37. http://www.columbia.edu/kermit/ckcbwr.html#contents + 38. http://www.columbia.edu/kermit/ckcbwr.html#x4 + 39. http://www.columbia.edu/kermit/ckcbwr.html#x2 + 40. http://www.columbia.edu/kermit/ckcbwr.html#top + 41. http://www.columbia.edu/kermit/ckcbwr.html#contents + 42. http://www.columbia.edu/kermit/ckcbwr.html#x5 + 43. http://www.columbia.edu/kermit/ckcbwr.html#x3 + 44. ftp://ftp.isi.edu/in-notes/rfc1122.txt + 45. http://www.columbia.edu/kermit/ckcbwr.html#top + 46. http://www.columbia.edu/kermit/ckcbwr.html#contents + 47. http://www.columbia.edu/kermit/ckcbwr.html#x6 + 48. http://www.columbia.edu/kermit/ckcbwr.html#x4 + 49. http://www.columbia.edu/kermit/ckcbwr.html#top + 50. http://www.columbia.edu/kermit/ckcbwr.html#contents + 51. http://www.columbia.edu/kermit/ckcbwr.html#x7 + 52. http://www.columbia.edu/kermit/ckcbwr.html#x5 + 53. http://www.columbia.edu/kermit/ck60manual.html + 54. http://www.columbia.edu/kermit/ckcbwr.html#x10 + 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#x8 + 58. http://www.columbia.edu/kermit/ckcbwr.html#x6 + 59. ftp://ftp.isi.edu/in-notes/rfc2217.txt + 60. http://www.columbia.edu/kermit/ckcbwr.html#top + 61. http://www.columbia.edu/kermit/ckcbwr.html#contents + 62. http://www.columbia.edu/kermit/ckcbwr.html#x9 + 63. http://www.columbia.edu/kermit/ckcbwr.html#x7 + 64. http://www.columbia.edu/kermit/ckcbwr.html#top + 65. http://www.columbia.edu/kermit/ckcbwr.html#contents + 66. http://www.columbia.edu/kermit/ckcbwr.html#x10 + 67. http://www.columbia.edu/kermit/ckcbwr.html#x8 + 68. http://www.columbia.edu/kermit/ckubwr.html + 69. http://www.columbia.edu/kermit/ckcbwr.html#top + 70. http://www.columbia.edu/kermit/ckcbwr.html#contents + 71. http://www.columbia.edu/kermit/ckcbwr.html#x11 + 72. http://www.columbia.edu/kermit/ckcbwr.html#x9 + 73. http://www.columbia.edu/kermit/ck60manual.html + 74. http://www.columbia.edu/kermit/ckermi70.htm + 75. http://www.columbia.edu/kermit/hp48.html + 76. http://www.columbia.edu/kermit/ckermit70.html#x4.22 + 77. http://www.columbia.edu/kermit/ckermit80.html#x15 + 78. http://www.columbia.edu/kermit/ckcbwr.html#top + 79. http://www.columbia.edu/kermit/ckcbwr.html#contents + 80. http://www.columbia.edu/kermit/ckcbwr.html#x10 + 81. http://www.columbia.edu/kermit/ckcbwr.html#top + 82. http://www.columbia.edu/kermit/ckcbwr.html#contents + 83. http://www.columbia.edu/kermit/ckermit.html + 84. http://www.columbia.edu/kermit/ck80.html + 85. http://www.columbia.edu/kermit/index.html + 86. http://www.columbia.edu/kermit/index.html + 87. http://www.columbia.edu/ + 88. mailto:kermit@columbia.edu diff --git a/ckccfg.txt b/ckccfg.txt new file mode 100644 index 0000000..5870974 --- /dev/null +++ b/ckccfg.txt @@ -0,0 +1,1766 @@ + + C-Kermit Configuration Options + + Frank da Cruz + [1]The Kermit Project + [2]Columbia University + + As of: C-Kermit 8.0.211, 10 April 2004 + This page last updated: Sun Apr 11 16:45:55 2004 (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: + + [3]http://www.columbia.edu/kermit/ckccfg.html + + [ [4]C-Kermit Home ] [ [5]Kermit Home ] + ________________________________________________________________________ + + CONTENTS + + 1. [6]FILE TRANSFER + 2. [7]SERIAL COMMUNICATION SPEEDS + 3. [8]FULLSCREEN FILE TRANSFER DISPLAY + 4. [9]CHARACTER SETS + 5. [10]APC EXECUTION + 6. [11]PROGRAM SIZE + 7. [12]MODEM DIALING + 8. [13]NETWORK SUPPORT + 9. [14]EXCEPTION HANDLING + 10. [15]SECURITY FEATURES + 11. [16]ENABLING SELECT() + 12. [17]I/O REDIRECTION + 13. [18]FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC + 14. [19]SPECIAL CONFIGURATIONS + I. [20]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 + [21]C-Kermit Installation Instructions for Unix. + + [ [22]C-Kermit Home ] [ [23]Kermit Home ] + ________________________________________________________________________ + + 1. FILE TRANSFER + + [ [24]Top ] [ [25]Contents ] [ [26]Next ] [ [27]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. + + [ [28]C-Kermit Home ] [ [29]Kermit Home ] + ________________________________________________________________________ + + 2. SERIAL COMMUNICATION SPEEDS + + [ [30]Top ] [ [31]Contents ] [ [32]Next ] [ [33]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 [34]ckutio.c for an example. + * TTSPDLIST is automatically defined in [35]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 [36]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 [37]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 [38]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 "depracated, legacy" + interfaces like ioctl()) and so the legal speed list must be + enumerated in the code -- see ttspdlist() in [39]ckutio.c. + + [ [40]C-Kermit Home ] [ [41]Kermit Home ] + ________________________________________________________________________ + + 3. FULLSCREEN FILE TRANSFER DISPLAY + + [ [42]Top ] [ [43]Contents ] [ [44]Next ] [ [45]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 recenlty, "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 + [46]makefile for "CK_CURSES" to see many examples, and also see the + relevant sections of the [47]Unix C-Kermit Installation Instructions, + particularly Sections [48]4 and [49]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 [50]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 [51]ckutio.c, + [52]ckufio.c, [53]ckuusx.c, [54]ckucmd.c, etc. + + [ [55]C-Kermit Home ] [ [56]Kermit Home ] + ________________________________________________________________________ + + 4. CHARACTER SETS + + [ [57]Top ] [ [58]Contents ] [ [59]Next ] [ [60]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. + + [61]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. + + [ [62]C-Kermit Home ] [ [63]Kermit Home ] + ________________________________________________________________________ + + 5. APC EXECUTION + + [ [64]Top ] [ [65]Contents ] [ [66]Next ] [ [67]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 [68]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 [69]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). + + [ [70]C-Kermit Home ] [ [71]Kermit Home ] + ________________________________________________________________________ + + 6. PROGRAM SIZE + + [ [72]Top ] [ [73]Contents ] [ [74]Next ] [ [75]Previous ] + + SECTION CONTENTS + + 6.1. [76]Feature Selection + 6.2. [77]Changing Buffer Sizes + 6.3. [78]Other Size-Related Items + 6.4. [79]Space/Time Tradeoffs + + (Also see [80]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: + + [81]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 [82]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 [83]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 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 platofrm 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 [84]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). + + [ [85]C-Kermit Home ] [ [86]Kermit Home ] + ________________________________________________________________________ + + 7. MODEM DIALING + + [ [87]Top ] [ [88]Contents ] [ [89]Next ] [ [90]Previous ] + + -DNODIAL removes automatic modem dialing completely, including the + entire [91]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 + + [ [92]C-Kermit Home ] [ [93]Kermit Home ] + ________________________________________________________________________ + + 8. NETWORK SUPPORT + + [ [94]Top ] [ [95]Contents ] [ [96]Next ] [ [97]Previous ] + + SECTION CONTENTS + + 8.1. [98]TCP/IP + 8.2. [99]X.25 + 8.3. [100]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. [101]Firewalls + 8.1.2. [102]Compilation and Linking Problems + 8.1.3. [103]Enabling Host Address Lists + 8.1.4. [104]Enabling Telnet NAWS + 8.1.5. [105]Enabling Incoming TCP/IP Connections + 8.1.6. [106]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 + [107]ckcnet.h, [108]ckctel.c, [109]ckctel.c, [110]ckctel.h, + [111]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 [112]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 [113]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 + [114]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 + [115]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 [116]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 regardting 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 + [117]kermit@columbia.edu if you are willing and able to work on such a + project. + + [ [118]C-Kermit Home ] [ [119]Kermit Home ] + ________________________________________________________________________ + + 9. EXCEPTION HANDLING + + [ [120]Top ] [ [121]Contents ] [ [122]Next ] [ [123]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 + + [ [124]C-Kermit Home ] [ [125]Kermit Home ] + ________________________________________________________________________ + + 10. SECURITY FEATURES + + [ [126]Top ] [ [127]Contents ] [ [128]Next ] [ [129]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 [130]Section 11 of the [131]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. + + [ [132]C-Kermit Home ] [ [133]Kermit Home ] + ________________________________________________________________________ + + 11. ENABLING SELECT() + + [ [134]Top ] [ [135]Contents ] [ [136]Next ] [ [137]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 [138]ckucns.c, and the fork-based one remains [139]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. + + [ [140]C-Kermit Home ] [ [141]Kermit Home ] + ________________________________________________________________________ + + 12. I/O REDIRECTION + + [ [142]Top ] [ [143]Contents ] [ [144]Next ] [ [145]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. + + [ [146]C-Kermit Home ] [ [147]Kermit Home ] + ________________________________________________________________________ + + 13. FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC + + [ [148]Top ] [ [149]Contents ] [ [150]Next ] [ [151]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 [152]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. + + [ [153]C-Kermit Home ] [ [154]Kermit Home ] + ________________________________________________________________________ + + 14. SPECIAL CONFIGURATIONS + + [ [155]Top ] [ [156]Contents ] [ [157]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. + + [ [158]C-Kermit Home ] [ [159]Kermit Home ] + ________________________________________________________________________ + + APPENDIX I: SUMMARY OF COMPILE-TIME OPTIONS + + [ [160]Top ] [ [161]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 [162]ckcdeb.h and + [163]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, Unkown, 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 avaible 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 compilcation + 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 [164]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()-releated 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. + + [ [165]Top ] [ [166]Contents ] [ [167]C-Kermit Home ] [ [168]Kermit + Home ] + _________________________________________________________________ + + + C-Kermit Configuration Options / [169]The Kermit Project / + [170]Columbia University / [171]kermit@columbia.edu / 14 March 2003 + +References + + 1. http://www.columbia.edu/kermit/ + 2. http://www.columbia.edu/ + 3. http://www.columbia.edu/kermit/ckccfg.html + 4. http://www.columbia.edu/kermit/ckermit.html + 5. http://www.columbia.edu/kermit/index.html + 6. http://www.columbia.edu/kermit/ckccfg.html#x1 + 7. http://www.columbia.edu/kermit/ckccfg.html#x2 + 8. http://www.columbia.edu/kermit/ckccfg.html#x3 + 9. http://www.columbia.edu/kermit/ckccfg.html#x4 + 10. http://www.columbia.edu/kermit/ckccfg.html#x5 + 11. http://www.columbia.edu/kermit/ckccfg.html#x6 + 12. http://www.columbia.edu/kermit/ckccfg.html#x7 + 13. http://www.columbia.edu/kermit/ckccfg.html#x8 + 14. http://www.columbia.edu/kermit/ckccfg.html#x9 + 15. http://www.columbia.edu/kermit/ckccfg.html#x10 + 16. http://www.columbia.edu/kermit/ckccfg.html#x11 + 17. http://www.columbia.edu/kermit/ckccfg.html#x12 + 18. http://www.columbia.edu/kermit/ckccfg.html#x13 + 19. http://www.columbia.edu/kermit/ckccfg.html#x14 + 20. http://www.columbia.edu/kermit/ckccfg.html#xa1 + 21. http://www.columbia.edu/kermit/ckuins.html + 22. http://www.columbia.edu/kermit/ckermit.html + 23. http://www.columbia.edu/kermit/index.html + 24. http://www.columbia.edu/kermit/ckccfg.html#top + 25. http://www.columbia.edu/kermit/ckccfg.html#contents + 26. http://www.columbia.edu/kermit/ckccfg.html#x2 + 27. http://www.columbia.edu/kermit/ckccfg.html#x0 + 28. http://www.columbia.edu/kermit/ckermit.html + 29. http://www.columbia.edu/kermit/index.html + 30. http://www.columbia.edu/kermit/ckccfg.html#top + 31. http://www.columbia.edu/kermit/ckccfg.html#contents + 32. http://www.columbia.edu/kermit/ckccfg.html#x3 + 33. http://www.columbia.edu/kermit/ckccfg.html#x1 + 34. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 35. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 36. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c + 37. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 38. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 39. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 40. http://www.columbia.edu/kermit/ckermit.html + 41. http://www.columbia.edu/kermit/index.html + 42. http://www.columbia.edu/kermit/ckccfg.html#top + 43. http://www.columbia.edu/kermit/ckccfg.html#contents + 44. http://www.columbia.edu/kermit/ckccfg.html#x4 + 45. http://www.columbia.edu/kermit/ckccfg.html#x2 + 46. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 47. http://www.columbia.edu/kermit/ckuins.html + 48. http://www.columbia.edu/kermit/ckuins.html#x4 + 49. http://www.columbia.edu/kermit/ckuins.html#x9.2 + 50. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c + 51. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 52. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 53. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c + 54. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c + 55. http://www.columbia.edu/kermit/ckermit.html + 56. http://www.columbia.edu/kermit/index.html + 57. http://www.columbia.edu/kermit/ckccfg.html#top + 58. http://www.columbia.edu/kermit/ckccfg.html#contents + 59. http://www.columbia.edu/kermit/ckccfg.html#x5 + 60. http://www.columbia.edu/kermit/ckccfg.html#x3 + 61. http://www.columbia.edu/kermit/unicode.html + 62. http://www.columbia.edu/kermit/ckermit.html + 63. http://www.columbia.edu/kermit/index.html + 64. http://www.columbia.edu/kermit/ckccfg.html#top + 65. http://www.columbia.edu/kermit/ckccfg.html#contents + 66. http://www.columbia.edu/kermit/ckccfg.html#x6 + 67. http://www.columbia.edu/kermit/ckccfg.html#x4 + 68. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h + 69. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h + 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#x7 + 75. http://www.columbia.edu/kermit/ckccfg.html#x5 + 76. http://www.columbia.edu/kermit/ckccfg.html#x6.1 + 77. http://www.columbia.edu/kermit/ckccfg.html#x6.2 + 78. http://www.columbia.edu/kermit/ckccfg.html#x6.3 + 79. http://www.columbia.edu/kermit/ckccfg.html#x6.4 + 80. http://www.columbia.edu/kermit/ckccfg.html#x4 + 81. http://www.columbia.edu/kermit/gkermit.html + 82. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h + 83. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h + 84. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 85. http://www.columbia.edu/kermit/ckermit.html + 86. http://www.columbia.edu/kermit/index.html + 87. http://www.columbia.edu/kermit/ckccfg.html#top + 88. http://www.columbia.edu/kermit/ckccfg.html#contents + 89. http://www.columbia.edu/kermit/ckccfg.html#x8 + 90. http://www.columbia.edu/kermit/ckccfg.html#x6 + 91. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c + 92. http://www.columbia.edu/kermit/ckermit.html + 93. http://www.columbia.edu/kermit/index.html + 94. http://www.columbia.edu/kermit/ckccfg.html#top + 95. http://www.columbia.edu/kermit/ckccfg.html#contents + 96. http://www.columbia.edu/kermit/ckccfg.html#x9 + 97. http://www.columbia.edu/kermit/ckccfg.html#x7 + 98. http://www.columbia.edu/kermit/ckccfg.html#x8.1 + 99. http://www.columbia.edu/kermit/ckccfg.html#x8.2 + 100. http://www.columbia.edu/kermit/ckccfg.html#x8.3 + 101. http://www.columbia.edu/kermit/ckccfg.html#x8.1.1 + 102. http://www.columbia.edu/kermit/ckccfg.html#x8.1.2 + 103. http://www.columbia.edu/kermit/ckccfg.html#x8.1.3 + 104. http://www.columbia.edu/kermit/ckccfg.html#x8.1.4 + 105. http://www.columbia.edu/kermit/ckccfg.html#x8.1.5 + 106. http://www.columbia.edu/kermit/ckccfg.html#x8.1.6 + 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h + 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c + 109. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c + 110. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h + 111. ftp://kermit.columbia.edu/kermit/c-kermit/ckcftp.c + 112. http://www.columbia.edu/kermit/ckermit.html + 113. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c + 114. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h + 115. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 116. http://www.columbia.edu/kermit/ckccfg.html#x11 + 117. mailto:kermit@columbia.edu + 118. http://www.columbia.edu/kermit/ckermit.html + 119. http://www.columbia.edu/kermit/index.html + 120. http://www.columbia.edu/kermit/ckccfg.html#top + 121. http://www.columbia.edu/kermit/ckccfg.html#contents + 122. http://www.columbia.edu/kermit/ckccfg.html#x10 + 123. http://www.columbia.edu/kermit/ckccfg.html#x8 + 124. http://www.columbia.edu/kermit/ckermit.html + 125. http://www.columbia.edu/kermit/index.html + 126. http://www.columbia.edu/kermit/ckccfg.html#top + 127. http://www.columbia.edu/kermit/ckccfg.html#contents + 128. http://www.columbia.edu/kermit/ckccfg.html#x11 + 129. http://www.columbia.edu/kermit/ckccfg.html#x9 + 130. http://www.columbia.edu/kermit/ckuins.html#x11 + 131. http://www.columbia.edu/kermit/ckuins.html + 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#x12 + 137. http://www.columbia.edu/kermit/ckccfg.html#x10 + 138. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c + 139. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c + 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#x13 + 145. http://www.columbia.edu/kermit/ckccfg.html#x11 + 146. http://www.columbia.edu/kermit/ckermit.html + 147. http://www.columbia.edu/kermit/index.html + 148. http://www.columbia.edu/kermit/ckccfg.html#top + 149. http://www.columbia.edu/kermit/ckccfg.html#contents + 150. http://www.columbia.edu/kermit/ckccfg.html#x14 + 151. http://www.columbia.edu/kermit/ckccfg.html#x12 + 152. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 153. http://www.columbia.edu/kermit/ckermit.html + 154. http://www.columbia.edu/kermit/index.html + 155. http://www.columbia.edu/kermit/ckccfg.html#top + 156. http://www.columbia.edu/kermit/ckccfg.html#contents + 157. http://www.columbia.edu/kermit/ckccfg.html#x13 + 158. http://www.columbia.edu/kermit/ckermit.html + 159. http://www.columbia.edu/kermit/index.html + 160. http://www.columbia.edu/kermit/ckccfg.html#top + 161. http://www.columbia.edu/kermit/ckccfg.html#contents + 162. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 163. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h + 164. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c + 165. http://www.columbia.edu/kermit/ckccfg.html#top + 166. http://www.columbia.edu/kermit/ckccfg.html#contents + 167. http://www.columbia.edu/kermit/ckermit.html + 168. http://www.columbia.edu/kermit/index.html + 169. http://www.columbia.edu/kermit/index.html + 170. http://www.columbia.edu/ + 171. mailto:kermit@columbia.edu diff --git a/ckcdeb.h b/ckcdeb.h new file mode 100644 index 0000000..31f9c55 --- /dev/null +++ b/ckcdeb.h @@ -0,0 +1,6369 @@ +/* C K C D E B . H */ + +/* + Tue Apr 6 14:00:16 2004 + + 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, 2004, + 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 inlcude all + material required by all other C-Kermit modules, including the non-Kermit + specific ones. +*/ + +#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 */ + +/* 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 */ + long 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 */ + long 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 */ +}; + +#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 */ + +#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 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 */ +#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. */ + +/* 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 */ + +#ifndef VOID /* Used throughout all C-Kermit */ +#ifdef CK_ANSIC /* modules... */ +#define VOID void +#else +#define VOID int +#endif /* CK_ANSIC */ +#endif /* VOID */ + +/* 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 */ + +#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 *, long)); +#define tlog(a,b,c,d) if (tralog && tlogfmt) dotlog(a,b,c,d) +_PROTOTYP(VOID doxlog,(int, char *, long, int, int, char *)); +#endif /* TLOG */ + +/* 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 +#ifdef HAVE_BAUDBOY /* Red Hat Linux >= 7.2 */ +#define USETTYLOCK +#endif /* HAVE_BAUDBOY */ +#endif /* USE_UU_LOCK */ +#endif /* AIXRS */ +#endif /* USETTYLOCK */ +#endif /* NOTTYLOCK */ + +/* 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 */ +/* Add more here... */ +#endif /* CKHTTP */ +#ifndef CKHTTP /* If CKHTTP not defined yet */ +#define NOHTTP /* then define HOHTTP */ +#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 */ + +/* 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 OS2 +#ifdef NT +#ifdef PCFONTS /* PC Font support */ +#undef PCFONTS +#endif /* PCFONTS */ +#ifdef NPIPE /* Named Pipes communication */ +#undef NPIPE +#endif /* NPIPE */ +#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"... +*/ + extern int errno; /* Needed by most modules. */ +#endif /* __GLIBC__ */ +#endif /* OS2 */ +#endif /* VMS */ +#endif /* STRATUS */ +#endif /* _CRAY */ + +#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 */ + +#ifndef DEBUG +/* Compile all the debug() statements away. Saves a lot of space and time. */ +#define debug(a,b,c,d) +#define hexdump(a,b,c) +/* Now define the debug() macro. */ +#else /* DEBUG */ +_PROTOTYP(int dodebug,(int,char *,char *,long)); +_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),(long)d) +#define hexdump(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),(long)d):0)) +#define hexdump(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),(long)d):0) +#define hexdump(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),(long)d) +#define hexdump(a,b,c) dohexdump((CHAR *)(a),(CHAR *)(b),c) +#endif /* IFDEBUG */ +#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 */ + +/* + 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( long zchki, (char *) ); +#ifdef VMSORUNIX +_PROTOTYP( long zgetfs, (char *) ); +#else +#ifdef OS2 +_PROTOTYP( long 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 *, long) ); +_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, (long) ) ; +#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 +#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 */ + +/* 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 MAXPATHLEN /* (it probably isn't) */ +#define CKMAXPATH MAXPATHLEN +#else +#ifdef PATH_MAX /* POSIX */ +#define CKMAXPATH PATH_MAX +#else +#ifdef MAC +#define CKMAXPATH 63 +#else +#ifdef pdp11 +#define CKMAXPATH 255 +#else +#ifdef UNIX /* Even though some are way less... */ +#define CKMAXPATH 1024 +#else +#ifdef VMS +#define CKMAXPATH 675 /* (derivation is complicated...) */ +#else +#ifdef STRATUS +#define CKMAXPATH 256 /* == $MXPL from PARU.H */ +#else +#ifdef datageneral +#define CKMAXPATH 256 /* == $MXPL from PARU.H */ +#else +#define CKMAXPATH 255 +#endif /* STRATUS */ +#endif /* datageneral */ +#endif /* VMS */ +#endif /* UNIX */ +#endif /* pdp11 */ +#endif /* MAC */ +#endif /* PATH_MAX */ +#endif /* MAXPATHLEN */ +#endif /* 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 NETPTY +_PROTOTYP( int do_pty, (char *)); +_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 */ diff --git a/ckcfn2.c b/ckcfn2.c new file mode 100644 index 0000000..06b97a2 --- /dev/null +++ b/ckcfn2.c @@ -0,0 +1,3297 @@ +/* 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, 2004, + 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, bctl, rsn, rln, maxtry, size; +extern int 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, ffc, flci, flco, tlci, tlco, tfc, speed; +extern long filcps, tfcps; + +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. */ +#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. */ +#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. +*/ + 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); + if (pkttyp != 'D') { /* Data packets would be too long */ + debug(F111,"spack data",data,data); + debug(F111,"spack d",d,d); + } + 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 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 */ + 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 / (long) 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. +*/ + 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(buf) char *buf; { + int buflen; + int len = -1; + CHAR chk; + char type = 0; + char *s = buf; + + if (!buf) return(0); + buflen = strlen(buf); + if (buflen < 5) return(0); /* Too short */ + 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; + if (xunchar(chk) != chk1((CHAR *)(buf+1),buflen-2)) /* Check it */ + return(0); + *s = chk; + 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 > -1 && 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. +*/ + if (type == 'I' || type == 'S') { /* 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; + 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) ","",(long) tsecs); + if (tsecs > 0) { + long lx; + lx = (tfc * 10L) / (long) tsecs; + tlog(F101," effective data rate ","",lx/10L); + } +#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 */ diff --git a/ckcfn3.c b/ckcfn3.c new file mode 100644 index 0000000..c499421 --- /dev/null +++ b/ckcfn3.c @@ -0,0 +1,2559 @@ +/* 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, 2004, + 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; +extern long sendstart, calibrate, 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 fsize, filcnt, ffc, tfc; + +#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 > -1L) { + sprintf((char *) &data[i+2],"%ld",x.lengthk); /* 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++; + } + } + /* File length in bytes */ + if (atleno && !done[xunchar(c = '1')] && x.length > -1L) { + sprintf((char *) &data[i+2],"%ld",x.length); /* 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++; + } + } +#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; long z; CHAR *p; + p = s + 1; + n = xunchar(*p++); + debug(F101,"rsattr RESEND n","",n); + z = 0L; + while (n-- > 0) /* We assume the format is good. */ + z = 10L * z + (long) (*p++ - '0'); + debug(F101,"rsattr RESEND z","",z); + if (z > 0L) sendstart = z; + debug(F101,"rsattr RESEND sendstart","",sendstart); + if (sendstart > 0L) + 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); +} + +long rs_len = 0L; /* Length of file being resent to */ + +/* + 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; + +#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) { + 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 = atol(abuf); /* Convert to number */ + break; + + 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 = 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 > 0L) { + 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 '1': /* File length in bytes */ + 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); + yy->length = atol(abuf); /* Convert to number */ + debug(F111,"gattr length",abuf,(int) yy->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 file length now, because we also need to know the file type */ + /* in case zchkspa() differentiates text and binary (VMS version does) */ + + if (atleni) { /* Length attribute enabled? */ + if (yy->length > -1L) { /* 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 > -1L) { /* Length in K attribute rec'd? */ + if (!zchkspa(ff,(yy->lengthk * 1024))) { + retcode = -1; /* Check space */ + *rp++ = '!'; + if (!opnerr) tlog(F100," refused: length K","",0); + } + } + } + if (yy->length > -1L) { /* Remember the file size */ + fsize = yy->length; + } else if (yy->lengthk > -1L) { + fsize = yy->lengthk * 1024L; + } else fsize = -1L; + +#ifdef DEBUG + if (deblog) { + sprintf(abuf,"%ld",fsize); /* safe */ + 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 */ + sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */ + 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 = -1L; + 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 = 0L; /* 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 > -1L) +#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 = 0L; + 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 = 0L; /* 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) { + long 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 = 0L; /* 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 = 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 */ diff --git a/ckcfns.c b/ckcfns.c new file mode 100644 index 0000000..53ea236 --- /dev/null +++ b/ckcfns.c @@ -0,0 +1,7108 @@ +char *fnsv = "C-Kermit functions, 8.0.223, 1 May 2003"; + +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, 2004, + 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 long ffc; +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 OS2ORUNIX +_PROTOTYP( long zfsize, (char *) ); +#endif /* OS2ORUNIX */ + +#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, 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, flci, flco, tlci, tlco, tfc, fsize, sendstart, rs_len; +extern long filrej, oldcps, cps, peakcps, ccu, ccp, calibrate, 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 */ + +/* 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 }; +long sndsmaller = -1L; +long sndlarger = -1L; + +/* 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 long 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 = 0L; + 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 == 0L && 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[1-swapping]; + output.bytes[1] = uc.x_char[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 == 0 && 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 == 0L) 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 = 0L; /* 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 == 0) { + 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 == 0) /* 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 == 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 == 0L) /* 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. +*/ + xx = (what & W_SEND) ? xut : xuf; + 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 = 0L; /* 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) { + 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 = 0L; /* File chars in and out */ +#ifdef GFTIMER + fpfsecs = 0.0; +#endif /* GFTIMER */ + tfc = tlci = tlco = 0L; /* Total file, line chars in & out */ + ccu = ccp = 0L; /* Control-char statistics */ +#ifdef COMMENT + fsize = -1L; /* File size */ +#else + if (!(what & W_SEND)) + fsize = -1L; + 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 */ +long ofn1len = 0L; +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; + int csave; + csave = calibrate; /* So we can decode filename */ + calibrate = 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 != -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 = 0L; /* Init per-file counters */ + cps = oldcps = 0L; + rs_len = 0L; + 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 > 0L) /* 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 = 0L; /* 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 > 4)) x = 1; + } + 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; long y = 0L; + 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 = -1L; /* Initialize file size */ + fullname[0] = NUL; + 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); + } +#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 = -1L; /* 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 */ + + y = -1L; /* Loop exit (file size) variable */ + while (y < 0L) { /* 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); + strncat(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) > -1) { + /* + 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 */ + y = zchki(fullname); /* Check if file readable */ + debug(F111,"gnfile zchki",fullname,y); + retcode = (int) y; /* Possible return code */ + if (y == -2L && dodirstoo) { + y = 0L; + } + if (y < 0L) { + gnferror = (int) y; + debug(F101,"gnfile gnferror C","",gnferror); + } + if (y == -1L) { /* 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 (y < 0) { + if (y == -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 = y; + xx = fileselect(fullname, + sndafter, sndbefore, + sndnafter,sndnbefore, + sndsmaller,sndlarger, + skipbup, + NSNDEXCEPT,sndexcept); + debug(F111,"gnfile fileselect",fullname,xx); + if (!xx) { + y = -1L; + 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); + y = -1L; + 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 long 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 */ + long 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%10ld %s %s\n", p, len, dstr, name); +#else + if (p) + sprintf((char *)linebuf, + "%10s%10ld %s %s%s%s\n", + p, len, dstr, name, + *lnk ? " -> " : "", + lnk + ); + else + sprintf((char *)linebuf, + "%10ld %s %s%s%s\n", + 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 == 1) + bytetag = "byte"; + sprintf((char *)funcbuf, + "%sSummary: %ld %s, %ld %s, %ld %s%s", + blankline, + ndirs, + dirtag, + nfiles, + filetag, + nbytes, + bytetag, + endline); +#else + sprintf((char *)funcbuf, + "%sSummary: %ld director%s, %ld file%s, %ld byte%s%s", + blankline, + ndirs, + (ndirs == 1) ? "y" : "ies", + nfiles, + (nfiles == 1) ? "" : "s", + nbytes, + (nbytes == 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 = 0L; + + 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. */ + rc = sinit(); + 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, %ld byte%s freed%s", + endline, + nfiles, + (nfiles == 1) ? "" : "s", + nbytes, + (nbytes == 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 = nbytes = 0L; + 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); + } + 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 */ diff --git a/ckcftp.c b/ckcftp.c new file mode 100644 index 0000000..d8d9ddd --- /dev/null +++ b/ckcftp.c @@ -0,0 +1,17126 @@ +/* C K C F T P -- FTP Client for C-Kermit */ + +char *ckftpv = "FTP Client, 8.0.226, 7 Jan 2004"; + +/* + Authors: + Jeffrey E Altman + Secure Endpoints Inc., New York City + Frank da Cruz , + The Kermit Project, Columbia University. + + Copyright (C) 2000, 2004, + 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" +#include +#include +#include +#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" + +/* + 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 */ + +#include +#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 SYSUTIMEH +#include +#else +#ifdef UTIMEH +#include +#define SYSUTIMEH +#endif /* UTIMEH */ +#endif /* SYSUTIMEH */ +#endif /* NOSETTIME */ + +#ifndef SCO_OSR504 +#ifdef SELECT_H +#include +#endif /* SELECT_H */ +#endif /* SCO_OSR504 */ + +/* select() dialects... */ + +#ifdef UNIX +#define BSDSELECT /* BSD select() syntax/semantics */ +#else +#ifdef OS2 /* OS/2 or Win32 */ +#ifdef NT +#define BSDSELECT +#else /* NT */ +#define IBMSELECT +#endif /* NT */ +#endif /* OS2 */ +#endif /* UNIX */ + +/* 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 +#define FTP_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; +#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 long fsize, ffc, tfc, filcnt, xfsecs, tfcps, cps, oldcps; +#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 long sendstart, sndsmaller, sndlarger, rs_len; + +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 */ + +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 long havesize = -1L; +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 short ftp_port = 0; /* FTP port */ +#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 + +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 }, + { "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) */ + +/* 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 */ + { "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); + strncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */ + strncat(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," ",NULL,NULL); + 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; + 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; long z; char * s; { + if (displa && fdispla && !backgrd && !quiet && !out2screen) { + if (!dpyactive) { + ckscreen(SCR_PT,'S',0L,""); + 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 */ + + case FTS_STO: { /* Server time offset */ + char * s, * p = NULL; + int 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); +} + +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 */ + int x; + char *s, * acct = ""; + + debok = 0; /* Don't log */ + if ((x = cmfld("Remote username or ID","",&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) + return(x); + 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); + 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 */ + +/* Used with recursive PUTs; Returns 0 on failure, 1 on success */ + +static int cdlevel = 0, cdsimlvl = 0; + +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; + 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); + 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) + *q = NUL; /* Keep just the path part */ + + debug(F110,"ftp syncdir buf",buf,0); + if (!strcmp(buf,putpath)) { /* Same as for previous file? */ + if (itsadir) { /* It's 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,0l,msgbuf); + } + /* See note above */ + return(0); + } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */ + return(0); + } else { + 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,0l,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 p",p,0); + debug(F110,"ftp syncdir A s",s,0); + + while (*p != NUL && *s != NUL && *p == *s) p++,s++; + + if (*s == '/' && !*p) s++; /* Don't count initial slash */ + + debug(F110,"ftp syncdir B p",p,0); + debug(F110,"ftp syncdir B s",s,0); + + /* p and s now point to the leftmost spot where they differ */ + + if (*s) { /* We have to back up */ + k = 1; /* How many levels */ + while ((c = *s++)) { /* Count dirseps */ + if (c == '/' && *s) + k++; + } + for (i = 0; i < k; i++) { /* Do that many CDUPs */ + debug(F111,"ftp syncdir up",p,i+1); + 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. */ + } + while (p > buf && *p && *p != '/') /* If in middle of segment */ + p--; /* back up to beginning */ + if (*p == '/') /* and terminate there */ + p++; + + 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,0l,msgbuf); + } + cdsimlvl++; + } else { + if (!doftpxmkd(p,0)) { /* Can't CD - try to create */ +/* + 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); + } + 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,0l,msgbuf); + } + if (!doftpcwd(p,0)) { /* Try again to CD */ + quiet = saveq; + return(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 +#ifdef V7 + struct utimbuf { + time_t timep[2]; + } tp; +#else +#ifdef SYSUTIMEH +#ifdef NT + struct _utimbuf tp; +#else /* NT */ + struct utimbuf tp; +#endif /* NT */ +#else + struct utimbuf { + time_t atime; + time_t mtime; + } tp; +#endif /* SYSUTIMEH */ +#endif /* V7 */ +#endif /* BSD44 */ + + 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 */ + tmlocal = gmtime(&statbuf.st_mtime); /* Convert to struct tm */ +#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 (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 (!pipename && (!local || !local[0])) + 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',(long)pktnum,remote); + ftscreen(SCR_AN,0,0L,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 */ + debug(F111,"ftp recvrequest rc",remote,rc); + if (cancelfile || cancelgroup) { + debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup); + ftscreen(SCR_ST,ST_INT,0l,""); + } else if (rc > 0) { + debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup); + ftscreen(SCR_ST,ST_SKIP,0l,cmarg); + } else if (rc < 0) { + switch (ftpcode) { + case -4: /* Network error */ + case -2: /* File error */ + ftscreen(SCR_ST,ST_MSG,0l,ck_errstr()); + break; + case -3: + ftscreen(SCR_ST,ST_MSG,0l,"Failure to make data connection"); + break; + case -1: + ftscreen(SCR_ST,ST_INT,0l,""); /* (should be covered above) */ + break; + default: + ftscreen(SCR_ST,ST_MSG,0l,&ftp_reply_str[4]); + } + } else { /* Tudo bem */ + ftscreen(SCR_PT,'Z',0L,""); + if (rc == 0) { + ftscreen(SCR_ST,ST_OK,0L,""); /* For screen */ + makestr(&rrfspec,remote); /* For WHERE command */ + makestr(&rfspec,fullname); + } + } + 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); + } 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; + +/* 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 = 0L; + 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 (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',(long)pktnum,local); + ftscreen(SCR_AN,0,0L,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); + ftscreen(SCR_ST,ST_SKIP,SKP_DAT,fullname); /* Skip this one */ + 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,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,0l,""); + } else if (rc > 0) { + debug(F101,"ftp put skipped",local,rc); + ftscreen(SCR_ST,ST_SKIP,rc,fullname); + } else if (rc < 0) { + debug(F111,"ftp put error",local,ftpcode); + ftscreen(SCR_ST,ST_MSG,0L,&ftp_reply_str[4]); + } else { + debug(F111,"ftp put not canceled",ckitoa(displa),fdispla); + ftscreen(SCR_PT,'Z',0L,""); + debug(F111,"ftp put ST_OK",local,rc); + ftscreen(SCR_ST,ST_OK,0L,""); + 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,0L,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,0L,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); +} + +/* 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); + 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',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(SCR_TC,0,0L,""); + + 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 { /* Temporary array for switch values */ + char * sval; + int ival; + } pv[SND_MAX+1]; + + 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 */ + } + 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_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: + if (!getval) break; + if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0) + goto xputx; + pv[n].ival = 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 && 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) { + y = zchki(s); + if (y < 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].ival; + if (pv[SND_LAR].ival > -1) + sndlarger = pv[SND_LAR].ival; + 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 = 0L; /* 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); + + 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(SCR_TC,0,0L,""); + } + 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 = -1L; + 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 = -1L; + if (sizeok) { + x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */ + if (x == REPLY_COMPLETE) + fsize = atol(&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 */ + + x = zchki(s2); + if (x > -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',0L,s); + ftscreen(SCR_ST,ST_SKIP,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++; + 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 > -1L) { /* 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 = -1L; + if (sizeok) { + x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); + if (x == REPLY_COMPLETE) + fsize = atol(&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 */ + + x = zchki(s2); + if (x > -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',0L,s); + ftscreen(SCR_ST,ST_SKIP,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++; + 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(SCR_TC,0,0L,""); + 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; + long getlarger = -1, getsmaller = -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 { /* Temporary array for switch values */ + char * sval; + int ival; + } pv[SND_MAX+1]; + + 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 = -1L; + 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 */ + } + 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: + if (!getval) break; + if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0) + goto xgetx; + pv[n].ival = 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 && 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 */ + + 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; + } +#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].ival > -1) + getsmaller = pv[SND_SMA].ival; + if (pv[SND_LAR].ival > -1) + getlarger = pv[SND_LAR].ival; + 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 = 0L; /* 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 (!(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 = 0L; /* 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',0L,s); + ftscreen(SCR_ST,ST_MSG,0L,"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 */ + int 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 = -1L; + + /* 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 > -1L || getlarger > -1L)) { + if (havesize > -1L) { /* 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 = -1L; + if (sizeok) { + x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm); + if (x == REPLY_COMPLETE) { + fsize = atol(&ftp_reply_str[4]); + gotsize = 1; + } + } + } + if (gotsize) { + if (getsmaller > -1L && fsize >= getsmaller) + skipthis++; + if (getlarger > -1L && 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',0L,s); + if (msg) + ftscreen(SCR_ST,ST_ERR,0L,msg); + else + ftscreen(SCR_ST,ST_SKIP,0L,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,0L,"Directory creation failure"); + break; + } + } + } + + /* Not skipping */ + + selected++; /* Count this file as selected */ + pn = NULL; + + if (!gotsize && !mdel) { /* Didn't get size yet */ + if (havesize > -1L) { /* Already have file size? */ + fsize = havesize; + gotsize = 1; + } else { /* No - must ask server */ + fsize = -1L; + if (sizeok) { + x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm); + if (x == REPLY_COMPLETE) { + fsize = atol(&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) { + x = zchki(s2); + debug(F111,"ftp get zchki",s2,x); + debug(F111,"ftp get x_fnc",s2,x_fnc); + + if (x > -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,0L,"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 cancelfile",s,cancelfile); + debug(F111,"ftp get cancelgroup",s,cancelgroup); + debug(F111,"ftp get renaming",s,renaming); + } +#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); + } else 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++; + if (geterror) { + status = 0; + ftscreen(SCR_EM,0,0L,"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 (x > -1) { /* 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(SCR_TC,0,0L,""); +#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; +#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 ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) { + do { + reply = getreply(0,lcs,rcs,ftp_vbm,0); + } while (reply == REPLY_PRELIM); + } + 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); + return(doftpcwd((char *)line,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 */ + 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,NULL,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) { + 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; + } + 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 succesfully.", + " /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_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_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.\n\n"); + return(0); + +#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); + 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(ftp_port); + } + } 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; + +#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) + 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 = atol(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 = 0L; + } else if (sendstart > fsize) { /* Remote file bigger than local */ + debug(F110,"doftpsend2 big size",ckltoa(fsize),sendstart); + sendstart = 0L; + } + /* 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 = 0L; /* Send the whole file */ + } + } + changetype(ftp_typ,0); /* Change back to appropriate type */ + if (sendstart > 0L) { /* Still restarting? */ + if (sendstart == fsize) { /* Same size - no need to send */ + debug(F111,"doftpsend2 /restart SKIP",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 = 0L; */ + } + } +#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); + + 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 */ + zclose(ZIFILE); +#ifdef PIPESEND + if (sndfilter) + pipesend = 0; +#endif /* PIPESEND */ + ftpsndret = -1; +#ifdef NTSIG + ckThreadEnd(threadinfo); +#endif /* NTSIG */ + return; + } + dout = dataconn(ftpsnd.lmode); /* Get data connection */ + if (dout == -1) { + failftpsend2(threadinfo); +#ifdef NTSIG + ckThreadEnd(threadinfo); +#endif /* NTSIG */ + return; + } + /* Initialize per-file stats */ + ffc = 0L; /* Character counter */ + cps = oldcps = 0L; /* Thruput */ +#ifdef GFTIMER + rftimer(); /* reset f.p. timer */ +#endif /* GFTIMER */ + +#ifdef SIGPIPE + ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + switch (curtype) { + case FTT_BIN: /* Binary mode */ + case FTT_TEN: + errno = d = 0; + while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) { + ftpsnd.bytes += n; + ffc += n; + debug(F111,"doftpsend2 zxin",ckltoa(n),ffc); + hexdump("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',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',spackets,NULL); + } + } +#ifdef CK_SSL + } +#endif /* CK_SSL */ + if (d <= 0) + break; + } + 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 */ + + if (openi(local) == 0) /* Try to open the input file */ + return(-1); + + 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; + long 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); + 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; + long bytes = 0L; + 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; + + 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",ckltoa(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 = 0L; /* 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; + } + if (c < 0) { + debug(F111,"ftp recvrequest errno",ckitoa(c),errno); + if (c == -1 && errno != EPIPE) + if ((!dpyactive || ftp_deb)) + perror("netin"); + bytes = -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) { + 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 = -1; + } + if (c == -2) + bytes = -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 = 0L; + + 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 = 0L; /* Local file size */ + rs_len = 0L; /* 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; + 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 != -1) { + 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,0L,"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,0L,"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 + +struct { + CONST gss_OID_desc * CONST * mech_type; + char *service_name; +} gss_trials[] = { + { &gss_mech_krb5, "ftp" }, + { &gss_mech_krb5, "host" }, +}; + +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 */ + +#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 != -1) { + 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 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 && 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 = -1L; /* 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',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; + + 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 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); + switch (error) { + case SSL_ERROR_NONE: + nin = bufp = count; + rpackets++; + pktnum++; + if (fdispla != XYFD_B) { + rpktl = count; + ftscreen(SCR_PT,'D',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',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 + */ +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); + nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0); + if (nin <= 0) { + 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); + hexdump("ftp secure_getc recv",ucbuf,16); + rpackets++; + pktnum++; + if (fdispla != XYFD_B) { + rpktl = nin; + ftscreen(SCR_PT,'D',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); + 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) */ diff --git a/ckcker.h b/ckcker.h new file mode 100644 index 0000000..b392b89 --- /dev/null +++ b/ckcker.h @@ -0,0 +1,1418 @@ +/* ckcker.h -- Symbol and macro definitions for C-Kermit */ + +/* + Author: Frank da Cruz , + Columbia University Academic Information Systems, New York City. + + Copyright (C) 1985, 2004, + 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, long) ); +_PROTOTYP( int z_line, (int, long) ); +_PROTOTYP( int z_getmode, (int) ); +_PROTOTYP( int z_getfnum, (int) ); +_PROTOTYP( long z_getpos, (int) ); +_PROTOTYP( long z_getline, (int) ); +_PROTOTYP( long z_count, (int, int) ); +_PROTOTYP( char * z_getname, (int) ); +_PROTOTYP( char * ckferror, (int) ); +#endif /* CKCHANNELIO */ + +_PROTOTYP( int scanfile, (char *, int *, int) ); + +/* 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, long, char *) ); +#ifdef VMS +#define xxscreen(a,b,c,d) \ +if (local && fdispla != XYFD_N) \ +ckscreen((int)a,(char)b,(long)c,(char *)d) +#else +#define xxscreen(a,b,c,d) \ +if (local && !backgrd && fdispla != XYFD_N) \ +ckscreen((int)a,(char)b,(long)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 */ + +/* 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, () ); +_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 *, + long, long, + 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 urldata { + char * sav; /* The URL itself */ + char * svc; /* Service */ + char * usr; /* User */ + char * psw; /* Password */ + char * hos; /* Host */ + char * por; /* Port */ + char * pth; /* Path */ +}; +_PROTOTYP(int urlparse, (char *, struct urldata *)); +#endif /* CK_URL */ + +#endif /* CKCKER_H */ + +/* End of ckcker.h */ diff --git a/ckclib.c b/ckclib.c new file mode 100644 index 0000000..36bf111 --- /dev/null +++ b/ckclib.c @@ -0,0 +1,2883 @@ +char * cklibv = "C-Kermit library, 8.0.033, 16 Mar 2003"; + +#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, 2004, + 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. + 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. + 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. + + 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 = 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 = 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 */ +/* ckltoa */ { + 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 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 */ + +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. +*/ +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; + +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 where = -1; + CHAR cp; /* Current character from pattern */ + CHAR cs; /* Current character from string */ + 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); + } + if (matchdepth == 0) { /* Top-level call? */ + 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 */ + 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; + } + } + if (!clist[(unsigned)cs]) { /* Match? */ + MATCHRETURN(5,0); /* Nope, done. */ + } + 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 (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; + 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); + while (*string && (k = ckmatch(p,string,icase,opts) < 1)) { + string++; + stringpos++; + } + debug(F111,"CKMATCH * k",string, k); + if (!matchpos && k > 0) { + matchpos = stringpos; + debug(F111,"CKMATCH *",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[96]; +#endif /* DEBUG */ + if (matchdepth > 0) + matchdepth--; + matchpos = rc; +#ifdef DEBUG + ckmakxmsg(msgbuf,96, + "CKMATCH RETURN[", + ckitoa(where), + "] matchpos=", + ckitoa(matchpos), + " matchdepth=", + ckitoa(matchdepth), + " string=",NULL,NULL,NULL,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 */ +} +#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; + unsigned long zz = 0L; + long z = 0L; + 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() */ +/* 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 */ + + 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 */ + if (n1 < 1) { /* If 0 (or less) */ + ck_sval.a_size = 0; /* nothing to do. */ + return(&ck_sval); + } + 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 = ""; + 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 (!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 (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') + ); + } + 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; + } + } + } + } + } + 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]; + 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; + } + 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 */ + wordnum++; /* Count it */ + if (splitting) { /* Dispose of it appropriately */ + if (wordnum > max) { /* Add to array if splitting */ + ck_sval.a_size = -2; + return(&ck_sval); + } + setword(wordnum,p,len); + } else if (wordnum == n) { /* Searching for word n */ + setword(1,p,len); + ck_sval.a_size = 1; + return(&ck_sval); + } + state = ST_BW; /* Switch to BETWEENWORDS state */ + len = 0; + } + break; + + case ST_IG: /* INGROUP */ + if (class & CL_CLS) { /* Have group closer? */ + 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) { + 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); + } + 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) { /* 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) { + setword(1,p+1,len); + ck_sval.a_size = 1; + return(&ck_sval); + } + } + if (!splitting) { /* Wanted word n but there was none? */ + setword(1,NULL,0); + ck_sval.a_size = 0; + return(&ck_sval); + } else { /* Succeed otherwise */ + ck_sval.a_size = wordnum; + 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); +} + +/* End of ckclib.c */ diff --git a/ckclib.h b/ckclib.h new file mode 100644 index 0000000..a258664 --- /dev/null +++ b/ckclib.h @@ -0,0 +1,100 @@ +/* ckclib.h -- C-Kermit library routine prototypes */ +/* + Author: Frank da Cruz , + Columbia University Academic Information Systems, New York City. + + Copyright (C) 2002, 2004, + 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 * ckitox, (int) ); +_PROTOTYP( char * ckltox, (long) ); +_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) ); + +#endif /* CKCLIB_H */ diff --git a/ckcmai.c b/ckcmai.c new file mode 100644 index 0000000..33504dc --- /dev/null +++ b/ckcmai.c @@ -0,0 +1,3592 @@ +#define EDITDATE "10 Apr 2004" /* Update these with each edit */ +#define EDITNDATE "20040410" /* Keep them in sync */ +/* Sat Apr 10 12:05:49 2004 */ + +/* + 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 be before any tests for preprocessor symbols. +*/ +#include "ckcsym.h" +/* + Consolidated program version information (for UNIX also see ckuver.h). + See makever() below for how they are used. +*/ +/* #ifdef COMMENT */ /* Uncomment this for test version */ +#ifndef OS2 +#ifndef BETATEST +#define BETATEST +#endif /* BETATEST */ +#endif /* OS2 */ +/* #endif */ /* COMMENT */ + +#ifdef BETATEST +#ifdef OS2 +#ifdef __DATE__ +#define BETADATE +#endif /* __DATE__ */ +#endif /* OS2 */ +#endif /* BETATEST */ + +#ifndef MAC +/* + 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 = ""; +#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 8.0.211"; +#endif /* UNIX */ + +char *ck_s_ver = "8.0.211"; /* C-Kermit version string */ +long ck_l_ver = 800211L; /* C-Kermit version number */ + +#ifdef OS2 +char *ck_s_xver = "2.2.0"; /* Product-specific version string */ +long ck_l_xver = 2200L; /* 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 Academic Information Systems, New York City. + +COPYRIGHT NOTICE: +*/ + +#ifdef OS2 +char *wiksdcpr[] = { +"Windows Internet Kermit Service Daemon (WIKSD):", +"Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New", +"York. All rights reserved.", +" ", +"PERMISSIONS:", +" ", +" The WIKSD software may be obtained directly, in binary form only, from", +" the Kermit Project at Columbia University by any individual for his or", +" her OWN USE, and by any company or other organization for its own", +" INTERNAL DISTRIBUTION and use, including installation on servers that", +" are accessed by customers or clients, WITHOUT EXPLICIT LICENSE. All", +" other forms of redistribution must be licensed from the Kermit Project", +" at Columbia University. These permissions apply only to the nonsecure", +" version of WIKSD.", +" ", +"DISCLAIMER:", +" ", +" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE", +" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS", +" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF", +" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER", +" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED", +" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.", +" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT", +" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,", +" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OR IN", +" CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS", +" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL", +" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN", +" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY", +" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING", +" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.", +" ", +"The above copyright notice, permissions notice, and disclaimer may not be", +"removed, altered, or obscured and shall be included in all copies of the", +"WIKSD software. The Trustees of Columbia University in the City of", +"New York reserve the right to revoke this permission if any of the terms", +"of use set forth above are breached.", +" ", +"For further information, contact the Kermit Project, Columbia University,", +"612 West 115th Street, New York NY 10025-7799, USA; Phone +1 (212) 854 3703,", +"Fax +1 (212) 662 6442, kermit@columbia.edu, http://www.columbia.edu/kermit/", +"" +}; +#endif /* OS2 */ + +char *copyright[] = { + +#ifdef pdp11 +"Copyright (C) 1985, 2004, Trustees of Columbia University, NYC.", +"All rights reserved.", +" ", +#else +#ifdef OS2 +"Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New", +"York. All rights reserved. This software is furnished under license", +"and may not be reproduced without license to do so. This copyright notice", +"must not be removed, altered, or obscured.", +" ", +" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE", +" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS", +" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF", +" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER", +" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED", +" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.", +" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT", +" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,", +" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OR IN", +" CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS", +" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL", +" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN", +" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY", +" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING", +" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.", +" ", +#else +"Copyright (C) 1985, 2004,", +" The Trustees of Columbia University in the City of New York.", +" All rights reserved.", +" ", +"PERMISSIONS:", +" ", +"The C-Kermit software may be obtained directly from the Kermit Project at", +"Columbia University (or from any source explicitly licensed by the Kermit", +"Project or implicitly licensed by Clause (A) below) by any individual for", +"his or her OWN USE, and by any company or other organization for its own", +"INTERNAL DISTRIBUTION and use, including installation on servers that are", +"accessed by customers or clients, WITHOUT EXPLICIT LICENSE.", +" ", +"Conditions for REDISTRIBUTION are as follows:", +" ", +"(A) The C-Kermit software, in source and/or binary form, may be", +" included WITHOUT EXPLICIT LICENSE in distributions of OPERATING", +" SYSTEMS that have OSI (Open Source Initiative, www.opensource.org)", +" approved licenses, even if non-Open-Source applications (but not", +" operating systems) are included in the same distribution. Such", +" distributions include, but are not limited to, CD-ROM, FTP site,", +" Web site, or preinstalled software on a new GENERAL-PURPOSE", +" computer, as long as the primary character of the distribution is", +" an Open Source operating system with accompanying utilities. The", +" C-Kermit source code may not be changed without the consent of the", +" Kermit Project, which will not be unreasonably withheld (this is", +" simply a matter of keeping a consistent and supportable code base).", +" ", +"(B) Inclusion of C-Kermit software in whole or in part, in any form, in", +" or with any product not covered by Clause (A), or its distribution", +" by any commercial enterprise to its actual or potential customers", +" or clients except as in Clause (A), requires a license from the", +" Kermit Project, Columbia University; contact kermit@columbia.edu.", +" ", +"The name of Columbia University may not be used to endorse or promote", +"products derived from or including the C-Kermit software without specific", +"prior written permission.", +" ", +"DISCLAIMER:", +" ", +" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE", +" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS", +" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF", +" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER", +" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED", +" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.", +" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT", +" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,", +" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR", +" IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS", +" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL", +" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN", +" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY", +" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING", +" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.", +" ", +"The above copyright notice, permissions notice, and disclaimer may not be", +"removed, altered, or obscured and shall be included in all copies of the", +"C-Kermit software. The Trustees of Columbia University in the City of", +"New York reserve the right to revoke this permission if any of the terms", +"of use set forth above are breached.", +#endif /* OS2 */ +#endif /* pdp11 */ + +#ifdef OS2 +"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, contact the Kermit Project, Columbia University,", +"612 West 115th Street, New York NY 10025-7799, USA; phone +1 (212) 854 3703,", +"fax +1 (212) 663 8202 or +1 (212) 662 6442, email kermit@columbia.edu,", +"Web http://www.columbia.edu/kermit/ or http://www.kermit-project.org/.", +#endif /* pdp11 */ +""}; + +/* +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). 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 + 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. + Marc Boucher, U of Montreal + Charles Brooks, EDN + Bob Brown + Mike Brown, Purdue U + 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 + 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 + 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 + 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 + 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) + 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 + 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 + 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) + APPP Scorer, Leeds Polytechnic, UK + Gordon Scott, Micro Focus, Newbury UK + Gisbert W. Selke, WIdO, Bonn, Germany + 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 + 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 + Dimitri Vulis, CUNY + Roger Wallace, Raytheon + Stephen Walton, Calif State U, Northridge (Amiga) + Jamie Watson, Adasoft, Switzerland (AIX) + Rick Watson, U of Texas (Macintosh) + 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 */ + 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 */ + +#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 */ + +int nzxopts = 0; /* Options for nzxpand() */ +int nfils = 0; /* Number of files in file group */ +long fsize = 0L; /* Size of current file */ +#ifdef UNIX +int wildxpand = 0; /* Who expands wildcards */ +#else /* UNIX */ +#ifdef STRATUS +int wildxpand = 1; +#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 */ + +long filcnt, /* Number of files in transaction */ + filrej, /* Number of files rejected in transaction */ + 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 */ + 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 */ + 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; + +long sendstart = 0L; /* SEND start position */ +long calibrate = 0L; /* Nonzero if calibration run */ + +#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... */ +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 VMS + rc = 0; + x = ckindex("[",path,0,0,0); /* 1-based */ + if (!x) + x = ckindex("<",path,0,0,0); + debug(F111,"isabsolute left bracket",path,x); + if (!x) { + x = ckindex(":",path,-1,1,1); + if (x) + debug(F111,"isabsolute logical",path,x); + } + if (x > 0) + if (path[x] != '.') /* 0-based */ + rc = 1; +#else +#ifdef UNIX + if (*path == '/' +#ifdef DTILDE + || *path == '~' +#endif /* DTILDE */ + ) + rc = 1; +#else +#ifdef OS2 + if (*path == '/' || *path == '\\') + rc = 1; + else if (isalpha(*path) && x > 2) + if (*(path+1) == ':' && (*(path +2) == '/' || *(path+2) == '\\')) + rc = 1; +#else +#ifdef AMIGA + if (*path == '/' +#ifdef DTILDE + || *path == '~' +#endif /* DTILDE */ + ) + rc = 1; +#else +#ifdef OSK + if (*path == '/' +#ifdef DTILDE + || *path == '~' +#endif /* DTILDE */ + ) + rc = 1; +#else +#ifdef datageneral + if (*path == ':') + rc = 1; +#else +#ifdef MAC + rc = 0; /* Fill in later... */ +#else +#ifdef STRATUS + rc = 0; /* Fill in later... */ +#else +#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 */ +#endif /* VMS */ + debug(F101,"isabsolute rc","",rc); + return(rc); +} + +/* 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_exit" */ + 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 */ + if (!cfilef) { + conoll("Interrupt 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); /* execute it */ + while (tlevel > -1) { /* until it runs out. */ + sstate = parser(1); /* Loop getting commands. */ + if (sstate) proto(); /* Enter protocol if requested. */ +#ifdef NTSIG + ck_ih(); +#endif /* NTSIG */ + } + cfilef = 1; /* Remember we did this */ + +#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 */ + if (!cfilef) { + conoll("Interrupt 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; +#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; + 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 */ + +/* Do some initialization */ + +#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 (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_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 */ diff --git a/ckcmdb.c b/ckcmdb.c new file mode 100644 index 0000000..de5cebc --- /dev/null +++ b/ckcmdb.c @@ -0,0 +1,387 @@ +/* + 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 */ diff --git a/ckcnet.c b/ckcnet.c new file mode 100644 index 0000000..3bae53e --- /dev/null +++ b/ckcnet.c @@ -0,0 +1,14205 @@ +char *cknetv = "Network support, 8.0.283, 7 Feb 2004"; + +/* C K C N E T -- Network support */ + +/* + Copyright (C) 1985, 2004, + 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 */ +#include +#include +#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 */ + +#ifdef NETCONN +/* Don't need any of this if there is no network support. */ + +/* + 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 */ +#include /* For inet_ntoa() prototype */ +#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 +#include /* Already included above */ +#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"; + hexdump(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"; + hexdump(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 */ + hexdump("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 */ + +#ifndef __DECC /* VAXC or GCC */ + +#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 /* __DECC */ +#endif /* DEC_TCPIP */ +#endif /* EXCELAN */ +#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. */ + if (ttnproto != NP_TCPRAW) + ttnproto = NP_NONE; /* No protocol selected yet. */ + 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. */ + if (ttnproto != NP_TCPRAW) + 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. */ +#ifdef COMMENT + /* Don't do this. */ + if (ttnproto != NP_TCPRAW) + 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 + (fd_set *) +#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) { + if (ttnproto != NP_TCPRAW) /* Yes and if raw port not requested */ + 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); + strncat(name,":",80-strlen(name)); + strncat(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 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 + static unsigned int saddrlen; +#else + static int saddrlen; +#endif /* VMS */ +#endif /* UNIXWARE */ +#endif /* AIX42 */ +#endif /* PTX */ + 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 */ +#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","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) + strncat(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; + 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: + ttnproto = NP_SSL; + 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. +*/ + +int +netopen(name, lcl, nett) char *name; int *lcl, nett; { + 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 */ +#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 */ +#ifndef datageneral + iax = (unsigned int) inet_addr(namecopy); +#else + iax = -1L; +#endif /* datageneral */ + debug(F111,"netopen inet_addr",namecopy,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 < 0 +#endif /* INADDR_NONE */ + ) { + if (!quiet) { + printf(" DNS Lookup... "); + fflush(stdout); + } + if ((host = gethostbyname(namecopy)) != NULL) { + debug(F100,"netopen 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] +#ifndef NOHTTP + && (tcp_http_proxy == NULL) +#endif /* NOHTTP */ + ) { /* Copying into our argument? */ + ckstrncpy(name,host->h_name,80); /* Bad Bad Bad */ + if ( (80-strlen(name)) > (strlen(svcbuf)+1) ) { + strncat(name,":",80-strlen(name)); + strncat(name,svcbuf,80-strlen(name)); + } + } + +#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 */ +#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); + 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,"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 */ + if (ttnproto != NP_TCPRAW && ttnproto != NP_NONE) + 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) ) { + strncat(name,":",80-strlen(name)); + strncat(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); + + 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) ) { + /* 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[] */ + hexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x); + ttibn += x; +#else /* OS2 */ +#ifdef CK_SSL + if ( ssl_active_flag || tls_active_flag ) { + hexdump("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,buf,n,0)) < 0) + return(-1); + else + return(len); + } +#endif /* RLOGCODE */ +#ifdef KRB5_U2U + if (ttnproto == NP_K5U2U) { + if ((len = krb5_u2u_read(ttyfd,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, +#ifndef __DECC + (fd_set *) +#endif /* __DECC */ + &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 */ + hexdump("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 + hexdump("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 */ + + 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 */ + + 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 */ + +#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 < 0 +#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 */ +#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); + 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); + + 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 + hexdump("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) { + 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 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); + 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) { + 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) { + 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 */ + 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 TCPIPLIB + x = socket_read(httpfd,&c,1); +#else + x = read(httpfd,&c,1); +#endif + + 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; + } + 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; + } + 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 + 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) { + 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; + } + 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 + 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) { + 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; + } + 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); + ckstrncat(request,"\r\n",len); + + /* 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; + } + 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; + int chunked = 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 = 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 */ diff --git a/ckcnet.h b/ckcnet.h new file mode 100644 index 0000000..c8d5576 --- /dev/null +++ b/ckcnet.h @@ -0,0 +1,1441 @@ +/* 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, 2004, + 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 */ +/* 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 netbreak, (void) ); +_PROTOTYP( int netinc, (int) ); +_PROTOTYP( int netxin, (int, CHAR *) ); +_PROTOTYP( int nettol, (CHAR *, int) ); +_PROTOTYP( int nettoc, (CHAR) ); +/* + 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 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 +#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 */ + +#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() */ + +#ifndef SOCKOPT_T +#define SOCKOPT_T int +#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 /* SOCKOPT_T */ + +/* Ditto for getsockname() */ + +#ifndef GSOCKNAME_T +#define GSOCKNAME_T int +#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 /* GSOCKNAME_T */ + +#endif /* TCPSOCKET */ + +#ifdef MACOSX10 +#ifdef bcopy +#undef bcopy +#endif +#ifdef bzero +#undef bzero +#endif +#endif /* MACOSX10 */ + +#endif /* CKCNET_H */ diff --git a/ckcplm.txt b/ckcplm.txt new file mode 100644 index 0000000..9c2f44f --- /dev/null +++ b/ckcplm.txt @@ -0,0 +1,3076 @@ + + C-Kermit Program Logic Manual + + Frank da Cruz + [1]The Kermit Project + [2]Columbia University + + As of: C-Kermit 8.0.211, 10 April 2004 + This page last updated: Sat Apr 10 16:45:30 2004 (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: + + [3]http://www.columbia.edu/kermit/ckcplm.html + + [ [4]C-Kermit Home ] [ [5]Kermit Home ] + ________________________________________________________________________ + + CONTENTS + + 1. [6]INTRODUCTION + 2. [7]FILES + 3. [8]SOURCE CODE PORTABILITY AND STYLE + 4. [9]MODULES + 4.A. [10]Group A: Library Routines + 4.B. [11]Group B: Kermit File Transfer + 4.C. [12]Group C: Character-Set Conversion + 4.D. [13]Group D: User Interface + 4.E. [14]Group E: Platform-Dependent I/O + 4.F. [15]Group F: Network Support + 4.G. [16]Group G: Formatted Screen Support + 4.H. [17]Group H: Pseudoterminal Support + 4.I. [18]Group I: Security + I. [19]APPENDIX I: FILE PERMISSIONS + ________________________________________________________________________ + + 1. INTRODUCTION + + The Kermit Protocol is specified in the book 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 [20]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. + 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"; + IPv6. + + [ [21]Contents ] [ [22]C-Kermit ] [ [23]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 Microsoft Windows NT/2000/XP + o OS/2 and/or Microsoft Windows 9x/ME/NT/2000/XP + p Plan 9 from Bell Labs + q (reserved) + r DEC PDP-11 with RSTS/E (never used, open for reassigment) + s Atari ST GEMDOS (last supported in version 5A(189)) + t DEC PDP-11 with RT-11 (never used, open for reassigment) + 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 + _ 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 [24]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). + + [ [25]Contents ] [ [26]C-Kermit ] [ [27]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-indendent 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)... + * Any variable whose value might exceed 16383 should be declared as + long, or if that is not possible, then as unsigned. + * 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 [28]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. This is increasingly true for 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: latter: + 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. + + [ [29]Contents ] [ [30]C-Kermit ] [ [31]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 [32]Section 4.A. + + [ [33]Contents ] [ [34]C-Kermit ] [ [35]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. + + [ [36]Contents ] [ [37]C-Kermit ] [ [38]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 communicatons 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). + + [ [39]Contents ] [ [40]C-Kermit ] [ [41]Kermit Home ] + ________________________________________________________________________ + + 4.A. Group A: Library Functions + + Library functions, strictly portable, can be used by all modules on + all platforms: [42]ckclib.h, [43]ckclib.c. + + (To be filled in... For now, see [44]Section 3.1 and the comments in + ckclib.c.) + + [ [45]Contents ] [ [46]C-Kermit ] [ [47]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: + + [48]ckcsym.h + For use by C compilers that don't allow -D on the command line. + + [49]ckcasc.h + ASCII character symbol definitions. + + [50]ckcsig.h + System-independent signal-handling definitions and prototypes. + + [51]ckcdeb.h + Originally, debugging definitions. Now this file also contains + all definitions and prototypes that are shared by all modules + in all groups. + + [52]ckcker.h + Kermit protocol symbol definitions. + + [53]ckcxla.h + Character-set-related symbol definitions (see next section). + + [54]ckcmai.c + The main program. This module contains the declarations of all + the protocol-related global variables that are shared among the + other modules. + + [55]ckcpro.w + The protocol module itself, written in "wart", a lex-like + preprocessor that is distributed with Kermit under the name + CKWART.C. + + [56]ckcfns.c, [57]ckcfn2.c, [58]ckcfn3.c + The protocol support functions used by the protocol module. + + [59]Group B modules may call upon functions from [60]Group E, but not + from [61]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.) + + [ [62]Contents ] [ [63]C-Kermit ] [ [64]Kermit Home ] + ________________________________________________________________________ + + 4.C. Group C: Character-Set Conversion + + Character set translation tables and functions. Used by the [65]Group + B, protocol modules, but may be specific to different computers. (So + far, all character character sets supported by C-Kermit are supported + in [66]ckuxla.c and [67]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. + + [68]ckcxla.h + Character-set definitions usable by all versions of C-Kermit. + + ck?xla.h + Character-set definitions for computer "?", e.g. [69]ckuxla.h + for UNIX, [70]ckmxla.h for Macintosh. + + [71]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. + + [72]ckcuni.h + Unicode definitions + + [73]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...) + + [ [74]Contents ] [ [75]C-Kermit ] [ [76]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 imbed the [77]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). + + [78]ckucmd.h + [79]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 [80]Group E functions. + + [81]ckuusr.h + Definitions of symbols used in Kermit's commands. + + ckuus*.c + Kermit's interactive command parser, including the script + programming language: [82]ckuusr.c (includes top-level keyword + tables); [83]ckuus2.c (HELP command text); [84]ckuus3.c (most + of the SET command); [85]ckuus4.c (includes variables and + functions); ckuus[567].c (miscellaneous); + + [86]ckuusy.c + The command-line-option parser. + + [87]ckuusx.c + User interface functions common to both the interactive and + command-line parsers. + + [88]ckuver.h + Version heralds for different implementations. + + [89]ckuscr.c + The (old, uucp-like) SCRIPT command + + [90]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: + [91]ckucon.c -- the traditional, portable, fork()-based version + -- and [92]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 [93]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 [94]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. + + [ [95]Contents ] [ [96]C-Kermit ] [ [97]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 [98]ckufio.c (files), [99]ckutio.c (communications), and + [100]ckusig.c (signal handling). + + For VMS, the files are [101]ckvfio.c, ckvtio.c, and [102]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 [103]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 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. + + [ [104]Contents ] [ [105]C-Kermit ] [ [106]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" ([107]ckutio.c for Unix), "v" + ([108]ckvtio.c for VMS), [109]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 /* Auxilliary 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 arugment 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 + [110]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 [111]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 cancelled 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 [112]ckcnet.h: + + NET_TCPB 1 TCP/IP Berkeley (socket) (implemented in [113]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-dependendent 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. + + [ [114]Contents ] [ [115]C-Kermit ] [ [116]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 [117]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: + + [118]ckcnet.h: Network-related symbol definitions. + [119]ckcnet.c: Network i/o (TCP/IP, X.25, etc), shared by most + platforms. + [120]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. + + [ [121]Contents ] [ [122]C-Kermit ] [ [123]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: + + [124]ckctel.h: Telnet protocol symbol definitions. + [125]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...) + ______________________________________________________________________ + + 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. + + [ [126]Contents ] [ [127]C-Kermit ] [ [128]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. + + [ [129]Contents ] [ [130]C-Kermit ] [ [131]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 + [132]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. + + [ [133]Contents ] [ [134]C-Kermit ] [ [135]Kermit Home ] + ________________________________________________________________________ + + 4.H. Group H: Pseudoterminal Support + + (To be filled in...) + ________________________________________________________________________ + + 4.I. Group I: Security + + (To be filled in...) + + [ [136]Contents ] [ [137]C-Kermit ] [ [138]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. + + [ [139]Top ] [ [140]Contents ] [ [141]C-Kermit Home ] [ [142]Kermit + Home ] + _________________________________________________________________ + + + C-Kermit Program Logic Manual / [143]The Kermit Project / + [144]Columbia University / [145]kermit@columbia.edu / 10 April 2004 + +References + + 1. http://www.columbia.edu/kermit/ + 2. http://www.columbia.edu/ + 3. http://www.columbia.edu/kermit/ckcplm.html + 4. http://www.columbia.edu/kermit/ckermit.html + 5. http://www.columbia.edu/kermit/index.html + 6. http://www.columbia.edu/kermit/ckcplm.html#x1 + 7. http://www.columbia.edu/kermit/ckcplm.html#x2 + 8. http://www.columbia.edu/kermit/ckcplm.html#x3 + 9. http://www.columbia.edu/kermit/ckcplm.html#x4 + 10. http://www.columbia.edu/kermit/ckcplm.html#x4.A + 11. http://www.columbia.edu/kermit/ckcplm.html#x4.B + 12. http://www.columbia.edu/kermit/ckcplm.html#x4.C + 13. http://www.columbia.edu/kermit/ckcplm.html#x4.D + 14. http://www.columbia.edu/kermit/ckcplm.html#x4.E + 15. http://www.columbia.edu/kermit/ckcplm.html#x4.F + 16. http://www.columbia.edu/kermit/ckcplm.html#x4.G + 17. http://www.columbia.edu/kermit/ckcplm.html#x4.H + 18. http://www.columbia.edu/kermit/ckcplm.html#x4.I + 19. http://www.columbia.edu/kermit/ckcplm.html#xa1 + 20. http://www.columbia.edu/kermit/ckcplm.html#contents + 21. http://www.columbia.edu/kermit/ckcplm.html#contents + 22. http://www.columbia.edu/kermit/ckermit.html + 23. http://www.columbia.edu/kermit/index.html + 24. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w + 25. http://www.columbia.edu/kermit/ckcplm.html#contents + 26. http://www.columbia.edu/kermit/ckermit.html + 27. http://www.columbia.edu/kermit/index.html + 28. http://www.columbia.edu/kermit/ckcplm.html#x3.2 + 29. http://www.columbia.edu/kermit/ckcplm.html#contents + 30. http://www.columbia.edu/kermit/ckermit.html + 31. http://www.columbia.edu/kermit/index.html + 32. http://www.columbia.edu/kermit/ckcplm.html#x4.A + 33. http://www.columbia.edu/kermit/ckcplm.html#contents + 34. http://www.columbia.edu/kermit/ckermit.html + 35. http://www.columbia.edu/kermit/index.html + 36. http://www.columbia.edu/kermit/ckcplm.html#contents + 37. http://www.columbia.edu/kermit/ckermit.html + 38. http://www.columbia.edu/kermit/index.html + 39. http://www.columbia.edu/kermit/ckcplm.html#contents + 40. http://www.columbia.edu/kermit/ckermit.html + 41. http://www.columbia.edu/kermit/index.html + 42. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.h + 43. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.c + 44. http://www.columbia.edu/kermit/ckcplm.html#x3.1 + 45. http://www.columbia.edu/kermit/ckcplm.html#contents + 46. http://www.columbia.edu/kermit/ckermit.html + 47. http://www.columbia.edu/kermit/index.html + 48. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsym.h + 49. ftp://kermit.columbia.edu/kermit/c-kermit/ckcasc.h + 50. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsig.h + 51. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 52. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h + 53. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h + 54. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c + 55. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w + 56. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfns.c + 57. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn2.c + 58. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn3.c + 59. http://www.columbia.edu/kermit/ckcplm.html#x4.B + 60. http://www.columbia.edu/kermit/ckcplm.html#x4.E + 61. http://www.columbia.edu/kermit/ckcplm.html#x4.D + 62. http://www.columbia.edu/kermit/ckcplm.html#contents + 63. http://www.columbia.edu/kermit/ckermit.html + 64. http://www.columbia.edu/kermit/index.html + 65. http://www.columbia.edu/kermit/ckcplm.html#x4.B + 66. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.c + 67. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h + 68. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h + 69. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h + 70. ftp://kermit.columbia.edu/kermit/c-kermit/ckmxla.h + 71. ftp://kermit.columbia.edu/kermit/c-kermit/ck?xla + 72. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.h + 73. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.c + 74. http://www.columbia.edu/kermit/ckcplm.html#contents + 75. http://www.columbia.edu/kermit/ckermit.html + 76. http://www.columbia.edu/kermit/index.html + 77. http://www.columbia.edu/kermit/ckcplm.html#x4.B + 78. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.h + 79. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c + 80. http://www.columbia.edu/kermit/ckcplm.html#x4.E + 81. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h + 82. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.c + 83. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus2.c + 84. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c + 85. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus4.c + 86. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusy.c + 87. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c + 88. ftp://kermit.columbia.edu/kermit/c-kermit/ckuver.h + 89. ftp://kermit.columbia.edu/kermit/c-kermit/ckuscr.c + 90. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c + 91. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c + 92. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c + 93. http://www.columbia.edu/kermit/ckcplm.html#x4.E + 94. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c + 95. http://www.columbia.edu/kermit/ckcplm.html#contents + 96. http://www.columbia.edu/kermit/ckermit.html + 97. http://www.columbia.edu/kermit/index.html + 98. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 99. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 100. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c + 101. ftp://kermit.columbia.edu/kermit/c-kermit/ckvfio.c + 102. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c + 103. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c + 104. http://www.columbia.edu/kermit/ckcplm.html#contents + 105. http://www.columbia.edu/kermit/ckermit.html + 106. http://www.columbia.edu/kermit/index.html + 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckvtio.c + 109. http://www.columbia.edu/kermit/ckcplm.html#x2 + 110. http://www.columbia.edu/kermit/ckcplm.html#xa1 + 111. http://www.columbia.edu/kermit/ckuins.html + 112. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h + 113. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 114. http://www.columbia.edu/kermit/ckcplm.html#contents + 115. http://www.columbia.edu/kermit/ckermit.html + 116. http://www.columbia.edu/kermit/index.html + 117. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 118. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h + 119. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c + 120. ftp://kermit.columbia.edu/kermit/c-kermit/cklnet.c + 121. http://www.columbia.edu/kermit/ckcplm.html#contents + 122. http://www.columbia.edu/kermit/ckermit.html + 123. http://www.columbia.edu/kermit/index.html + 124. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h + 125. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.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. http://www.columbia.edu/kermit/ckcplm.html#contents + 130. http://www.columbia.edu/kermit/ckermit.html + 131. http://www.columbia.edu/kermit/index.html + 132. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c + 133. http://www.columbia.edu/kermit/ckcplm.html#contents + 134. http://www.columbia.edu/kermit/ckermit.html + 135. http://www.columbia.edu/kermit/index.html + 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. http://www.columbia.edu/kermit/ckcplm.html#top + 140. http://www.columbia.edu/kermit/ckcplm.html#contents + 141. http://www.columbia.edu/kermit/ckermit.html + 142. http://www.columbia.edu/kermit/index.html + 143. http://www.columbia.edu/kermit/index.html + 144. http://www.columbia.edu/ + 145. mailto:kermit@columbia.edu diff --git a/ckcpro.w b/ckcpro.w new file mode 100644 index 0000000..cd9d1fa --- /dev/null +++ b/ckcpro.w @@ -0,0 +1,3591 @@ +char *protv = /* -*-C-*- */ +"C-Kermit Protocol Module 8.0.158, 11 Sep 2002"; + +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, 2004, + 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 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)\n", success ? "OK" : "FAILED"); + } + break; + case 'r': + case 'v': + if (rrfspec) { + printf(" RCVD: [%s]",rrfspec); + if (rfspec) + printf(" To: [%s]",rfspec); + printf(" (%s)\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)\n", success ? "OK" : "FAILED"); + } + break; + case 'r': + case 'v': + if (rrfspec) { + printf(" RCVD: (%d files)",n); + if (rfspec) + printf(" Last: [%s]",rfspec); + printf(" (%s)\n", success ? "OK" : "FAILED"); + } + } + } else if (n == 0) { + if (myjob == 's') + printf(" SENT: (0 files) \n"); + else if (myjob == 'r' || myjob == 'v') + printf(" RCVD: (0 files) \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. */ +#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. */ +} + +/* + 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 - 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 */ +} + +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(NULL)) { + 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); +} + +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; + 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 */ + 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') { + if ((int)strlen(s) + (int)strlen(fspec) < tmpbufsiz) { + sprintf(tmpbuf,s,fspec); /* safe (prechecked) */ + tlog(F110,"Sending",fspec,0L); + } + } else { + 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 */ diff --git a/ckcsig.h b/ckcsig.h new file mode 100644 index 0000000..30fb8bf --- /dev/null +++ b/ckcsig.h @@ -0,0 +1,214 @@ +/* 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, 2004, + 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 */ diff --git a/ckcssl.h b/ckcssl.h new file mode 100644 index 0000000..4e31f12 --- /dev/null +++ b/ckcssl.h @@ -0,0 +1,126 @@ +#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 */ diff --git a/ckcsym.h b/ckcsym.h new file mode 100644 index 0000000..c340ca3 --- /dev/null +++ b/ckcsym.h @@ -0,0 +1,5 @@ +/* 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. + */ diff --git a/ckctel.c b/ckctel.c new file mode 100644 index 0000000..f743820 --- /dev/null +++ b/ckctel.c @@ -0,0 +1,8946 @@ +char *cktelv = "Telnet support, 8.0.269, 4 Mar 2004"; +#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, 2004, + 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 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 */ + + 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 (tn_wait_idx) { + hexdump((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; + + /* 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. */ + + 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(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,fwdx_msg_out,"",0); + if (tn_deb || debses) tn_debug(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(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,fwdx_msg_out,"",0); + if (tn_deb || debses) tn_debug(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( 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(fwdx_msg_out,TN_MSG_LEN,&sb_priv[j_sav],j-(j_sav+2)); + ckstrncat(fwdx_msg_out," IAC SE",TN_MSG_LEN); + } +#endif /* DEBUG */ +#ifdef OS2 + RequestTelnetMutex( SEM_INDEFINITE_WAIT ); +#endif +#ifdef DEBUG + debug(F100,fwdx_msg_out,"",0); + if (tn_deb || debses) tn_debug(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( 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(fwdx_msg_out,TN_MSG_LEN,&sb_priv[6],j-8); + ckstrncat(fwdx_msg_out," IAC SE",TN_MSG_LEN); + } +#endif /* DEBUG */ +#ifdef OS2 + RequestTelnetMutex( SEM_INDEFINITE_WAIT ); +#endif +#ifdef DEBUG + debug(F100,fwdx_msg_out,"",0); + if (tn_deb || debses) tn_debug(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(fwdx_msg_out,"TELNET SENT SB %s OPTION_DATA XAUTH ", + TELOPT(TELOPT_FORWARD_X)); + tn_hex(fwdx_msg_out,TN_MSG_LEN,&sb_priv[5],(p-sb_priv)-7); + ckstrncat(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,fwdx_msg_out,"",0); + if (tn_deb || debses) tn_debug(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 (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; + + 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 (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 (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); + case NP_NONE: + case NP_SSL: + case NP_TLS: /* If not talking to a telnet port, */ + 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); + 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; + 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(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 */ + + +/* 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)) { + hexdump("unknown telnet subnegotiation",sb,n); + break; + } else if ( !(TELOPT_ME(x) || TELOPT_U(x)) ) { + hexdump("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 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(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; + + 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; + } + + hexdump((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 (!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 = 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 (!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 (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; + + 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; + 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 */ diff --git a/ckctel.h b/ckctel.h new file mode 100644 index 0000000..c05b3fb --- /dev/null +++ b/ckctel.h @@ -0,0 +1,1259 @@ +/* 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, 2004, + 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: + . Only one source file should include #defines for + TELCMDS, TELOPTS, TELOPT_STATES, SLC_NAMES, and AUTH_NAMES. + . This file should be used inplace of "arpa/telnet.h" +*/ + +#ifndef CKCTEL_H +#define CKCTEL_H +#ifdef TNCODE +/* + Definitions for the TELNET protocol. + can't rely on library header files for any of them. +*/ +#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 */ + +#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, (VOID)); +_PROTOTYP( int fwdx_server_avail, (VOID)); +#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 */ diff --git a/ckcuni.c b/ckcuni.c new file mode 100644 index 0000000..b579510 --- /dev/null +++ b/ckcuni.c @@ -0,0 +1,16173 @@ +char * ckcuni = "Unicode support 8.0.115, 9 Oct 2002"; + +#ifdef OS2 +#define KERMITFONT +#endif /* OS2 */ + +/* C K C U N I . C -- Unicode/Terminal character-set translations */ + +/* + Copyright (C) 1999, 2004, + 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 */ diff --git a/ckcuni.h b/ckcuni.h new file mode 100644 index 0000000..df978c2 --- /dev/null +++ b/ckcuni.h @@ -0,0 +1,268 @@ +/* C K C U N I . H -- Unicode/Terminal character-set translations */ + +/* + Copyright (C) 1999, 2004, + 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 */ diff --git a/ckcxla.h b/ckcxla.h new file mode 100644 index 0000000..e5dc887 --- /dev/null +++ b/ckcxla.h @@ -0,0 +1,334 @@ +/* + 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, 2004, + 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 */ + +#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 */ diff --git a/ckermit.ini b/ckermit.ini new file mode 100644 index 0000000..f007561 --- /dev/null +++ b/ckermit.ini @@ -0,0 +1,618 @@ +COMMENT - 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. diff --git a/ckermit70.txt b/ckermit70.txt new file mode 100644 index 0000000..8aca397 --- /dev/null +++ b/ckermit70.txt @@ -0,0 +1,17956 @@ + + [ [1]Contents ] [ [2]C-Kermit ] [ [3]Kermit Home ] + + Supplement to Using C-Kermit, Second Edition + +For C-Kermit 7.0 + +As of C-Kermit version: 7.0.196 +This file last updated: 8 February 2000 + +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: [4]kermit-support@columbia.edu +Web: [5]http://www.columbia.edu/kermit/ +Or: [6]http://www.kermit-project.org/ +Or: [7]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 [8]COPYING.TXT file or the + copyright text in the [9]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 + [10]Appendix V. + _________________________________________________________________ + + WHAT IS IN THIS FILE + + This file lists changes made to C-Kermit since the second edition of + the book [11]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 [12]CKCBWR.TXT ("C-Kermit + beware") file for hints, tips, tricks, restrictions, frequently asked + questions, etc, plus the system-specific "beware file", e.g. + [13]CKUBWR.TXT for UNIX, [14]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: [15]ckubwr.txt, [16]ckermit70.txt, etc. + _________________________________________________________________ + + ADDITIONAL FILES + + Several other files accompany this new Kermit release: + + SECURITY.TXT + Discussion of Kermit's new authentication and encryption + features: + + + [17]Plain-text version + + [18]HTML (hypertext) version + + IKSD.TXT + How to install and manage an Internet Kermit Service Daemon. + + + [19]Plain-text version + + [20]HTML (hypertext) version + + Also see [21]cuiksd.htm for instructions for use. + + TELNET.TXT + A thorough presentation of Kermit's new advanced Telnet + features and controls. + + + [22]Plain-text version + + [23]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 [24]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 + [25]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 ([26]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 [27]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. [28]C-KERMIT DOCUMENTATION + + II. [29]NEW FEATURES + + (0) [30]INCOMPATIBILITIES WITH PREVIOUS RELEASES + (1) [31]PROGRAM AND FILE MANAGEMENT AND COMMANDS + 1.0. [32]Bug fixes + 1.1. [33]Command Continuation + 1.2. [34]Editor Interface + 1.3. [35]Web Browser and FTP Interface + 1.4. [36]Command Editing + 1.5. [37]Command Switches + 1.5.1. [38]General Switch Syntax + 1.5.2. [39]Order and Effect of Switches + 1.5.3. [40]Distinguishing Switches from Other Fields + 1.5.4. [41]Standard File Selection Switches + 1.5.5. [42]Setting Preferences for Different Commands + 1.6. [43]Dates and Times + 1.7. [44]Partial Completion of Keywords + 1.8. [45]Command Recall + 1.9. [46]EXIT Messages + 1.10. [47]Managing Keyboard Interruptions + 1.11. [48]Taming the Wild Backslash -- Part Deux + 1.11.1. [49]Background + 1.11.2. [50]Kermit's Quoting Rules + 1.11.3. [51]Passing DOS Filenames from Kermit to Shell Commands + 1.11.4. [52]Using Variables to Hold DOS Filenames + 1.11.5. [53]Passing DOS Filenames as Parameters to Macros + 1.11.6. [54]Passing DOS File Names from Macro Parameters to the +DOS Shell + 1.11.7. [55]Passing DOS Filenames to Kermit from the Shell + 1.12. [56]Debugging + 1.13. [57]Logs + 1.14. [58]Automatic File-Transfer Packet Recognition at the Command Pr +ompt + 1.15. [59]The TYPE Command + 1.16. [60]The RESET Command + 1.17. [61]The COPY and RENAME Commands + 1.18. [62]The MANUAL Command + 1.19. [63]String and Filename Matching Patterns + 1.20. [64]Multiple Commands on One Line + 1.21. [65]What Do I Have? + 1.22. [66]Generalized File Input and Output + 1.22.1. [67]Why Another I/O System? + 1.22.2. [68]The FILE Command + 1.22.3. [69]FILE Command Examples + 1.22.4. [70]Channel Numbers + 1.22.5. [71]FILE Command Error Codes + 1.22.6. [72]File I/O Variables + 1.22.7. [73]File I/O Functions + 1.22.8. [74]File I/O Function Examples + 1.23. [75]The EXEC Command + 1.24. [76]Getting Keyword Lists with '?' + (2) [77]MAKING AND USING CONNECTIONS + 2.0. [78]SET LINE and SET HOST Command Switches + 2.1. [79]Dialing + 2.1.1. [80]The Dial Result Message + 2.1.2. [81]Long-Distance Dialing Changes + 2.1.3. [82]Forcing Long-Distance Dialing + 2.1.4. [83]Exchange-Specific Dialing Decisions + 2.1.5. [84]Cautions about Cheapest-First Dialing + 2.1.6. [85]Blind Dialing (Dialing with No Dialtone) + 2.1.7. [86]Trimming the Dialing Dialog + 2.1.8. [87]Controlling the Dialing Speed + 2.1.9. [88]Pretesting Phone Number Conversions + 2.1.10. [89]Greater Control over Partial Dialing + 2.1.11. [90]New DIAL-related Variables and Functions + 2.1.12. [91]Increased Flexibility of PBX Dialing + 2.1.13. [92]The DIAL macro - Last-Minute Phone Number Conversions + 2.1.14. [93]Automatic Tone/Pulse Dialing Selection + 2.1.15. [94]Dial-Modifier Variables + 2.1.16. [95]Giving Multiple Numbers to the DIAL Command + 2.2. [96]Modems + 2.2.1. [97]New Modem Types + 2.2.2. [98]New Modem Controls + 2.3. [99]TELNET and RLOGIN + 2.3.0. [100]Bug Fixes + 2.3.1. [101]Telnet Binary Mode Bug Adjustments + 2.3.2. [102]VMS UCX Telnet Port Bug Adjustment + 2.3.3. [103]Telnet New Environment Option + 2.3.4. [104]Telnet Location Option + 2.3.5. [105]Connecting to Raw TCP Sockets + 2.3.6. [106]Incoming TCP Connections + 2.4. [107]The EIGHTBIT Command + 2.5. [108]The Services Directory + 2.6. [109]Closing Connections + 2.7. [110]Using C-Kermit with External Communication Programs + 2.7.0. [111]C-Kermit over tn3270 and tn5250 + 2.7.1. [112]C-Kermit over Telnet + 2.7.2. [113]C-Kermit over Rlogin + 2.7.3. [114]C-Kermit over Serial Communication Programs + 2.7.4. [115]C-Kermit over Secure Network Clients + 2.7.4.1. [116]SSH + 2.7.4.2. [117]SSL + 2.7.4.3. [118]SRP + 2.7.4.4. [119]SOCKS + 2.7.4.5. [120]Kerberos and SRP + 2.8. [121]Scripting Local Programs + 2.9. [122]X.25 Networking + 2.9.1. [123]IBM AIXLink/X.25 Network Provider Interface for AIX + 2.9.2. [124]HP-UX X.25 + 2.10. [125]Additional Serial Port Controls + 2.11. [126]Getting Access to the Dialout Device + 2.12. [127]The Connection Log + 2.13. [128]Automatic Connection-Specific Flow Control Selection + 2.14. [129]Trapping Connection Establishment and Loss + 2.15. [130]Contacting Web Servers with the HTTP Command + (3) [131]TERMINAL CONNECTION + 3.1. [132]CONNECT Command Switches + 3.2. [133]Triggers + 3.3. [134]Transparent Printing + 3.4. [135]Binary and Text Session Logs + (4) [136]FILE TRANSFER AND MANAGEMENT + 4.0. [137]Bug Fixes, Minor Changes, and Clarifications + 4.1. [138]File-Transfer Filename Templates + 4.1.1. [139]Templates in the As-Name + 4.1.2. [140]Templates on the Command Line + 4.1.3. [141]Post-Transfer Renaming + 4.2. [142]File-Transfer Pipes and Filters + 4.2.1. [143]Introduction + 4.2.1.1. [144]Terminology + 4.2.1.2. [145]Notation + 4.2.1.3. [146]Security + 4.2.2. [147]Commands for Transferring from and to Pipes + 4.2.2.1. [148]Sending from a Command + 4.2.2.2. [149]Receiving to a Command + 4.2.3. [150]Using File-Transfer Filters + 4.2.3.1. [151]The SEND Filter + 4.2.3.2. [152]The RECEIVE Filter + 4.2.4. [153]Implicit Use of Pipes + 4.2.5. [154]Success and Failure of Piped Commands + 4.2.6. [155]Cautions about Using Pipes to Transfer Directory Trees + 4.2.7. [156]Pipes and Encryption + 4.2.8. [157]Commands and Functions Related to Pipes + 4.2.8.1. [158]The OPEN !READ and OPEN !WRITE Commands + 4.2.8.2. [159]The REDIRECT Command + 4.2.8.3. [160]Receiving Mail and Print Jobs + 4.2.8.4. [161]Pipe-Related Functions + 4.3. [162]Automatic Per-File Text/Binary Mode Switching + 4.3.1. [163]Exceptions + 4.3.2. [164]Overview + 4.3.3. [165]Commands + 4.3.4. [166]Examples + 4.4. [167]File Permissions + 4.4.1. [168]When ATTRIBUTES PROTECTION is OFF + 4.4.1.1. [169]Unix + 4.4.1.2. [170]VMS + 4.4.2. [171]When ATTRIBUTES PROTECTION is ON + 4.4.2.1. [172]System-Specific Permissions + 4.4.2.1.1. [173]UNIX + 4.4.2.1.2. [174]VMS + 4.4.2.2. [175]System-Independent Permissions + 4.5. [176]File Management Commands + 4.5.1. [177]The DIRECTORY Command + 4.5.2. [178]The CD and BACK Commands + 4.5.2.1. [179]Parsing Improvements + 4.5.2.2. [180]The CDPATH + 4.5.3. [181]Creating and Removing Directories + 4.5.4. [182]The DELETE and PURGE Commands + 4.6. [183]Starting the Remote Kermit Server Automatically + 4.7. [184]File-Transfer Command Switches + 4.7.1. [185]SEND Command Switches + 4.7.2. [186]GET Command Switches + 4.7.3. [187]RECEIVE Command Switches + 4.8. [188]Minor Kermit Protocol Improvements + 4.8.1. [189]Multiple Attribute Packets + 4.8.2. [190]Very Short Packets + 4.9. [191]Wildcard / File Group Expansion + 4.9.1. [192]In UNIX C-Kermit + 4.9.2. [193]In Kermit 95 + 4.9.3. [194]In VMS, AOS/VS, OS-9, VOS, etc. + 4.10. [195]Additional Pathname Controls + 4.11. [196]Recursive SEND and GET: Transferring Directory Trees + 4.11.1. [197]Command-Line Options + 4.11.2. [198]The SEND /RECURSIVE Command + 4.11.3. [199]The GET /RECURSIVE Command + 4.11.4. [200]New and Changed File Functions + 4.11.5. [201]Moving Directory Trees Between Like Systems + 4.11.6. [202]Moving Directory Trees Between Unlike Systems + 4.12. [203]Where Did My File Go? + 4.13. [204]File Output Buffer Control + 4.14. [205]Improved Responsiveness + 4.15. [206]Doubling and Ignoring Characters for Transparency + 4.16. [207]New File-Transfer Display Formats + 4.17. [208]New Transaction Log Formats + 4.17.1. [209]The BRIEF Format + 4.17.2. [210]The FTP Format + 4.18. [211]Unprefixing NUL + 4.19. [212]Clear-Channel Protocol + 4.20. [213]Streaming Protocol + 4.20.1. [214]Commands for Streaming + 4.20.2. [215]Examples of Streaming + 4.20.2.1. [216]Streaming on Socket-to-Socket Connections + 4.20.2.2. [217]Streaming on Telnet Connections + 4.20.2.3. [218]Streaming with Limited Packet Length + 4.20.2.4. [219]Streaming on Dialup Connections + 4.20.2.5. [220]Streaming on X.25 Connections + 4.20.3. [221]Streaming - Preliminary Conclusions + 4.21. [222]The TRANSMIT Command + 4.22. [223]Coping with Faulty Kermit Implementations + 4.22.1. [224]Failure to Accept Modern Negotiation Strings + 4.22.2. [225]Failure to Negotiate 8th-bit Prefixing + 4.22.3. [226]Corrupt Files + 4.22.4. [227]Spurious Cancellations + 4.22.5. [228]Spurious Refusals + 4.22.6. [229]Failures during the Data Transfer Phase + 4.22.7. [230]Fractured Filenames + 4.22.8. [231]Bad File Dates + 4.23. [232]File Transfer Recovery + 4.24. [233]FILE COLLISION UPDATE Clarification + 4.25. [234]Autodownload Improvements + (5) [235]CLIENT/SERVER + 5.0. [236]Hints + 5.1. [237]New Command-Line Options + 5.2. [238]New Client Commands + 5.3. [239]New Server Capabilities + 5.3.1. [240]Creating and Removing Directories + 5.3.2. [241]Directory Listings + 5.4. [242]Syntax for Remote Filenames with Embedded Spaces + 5.5. [243]Automatic Orientation Messages upon Directory Change + 5.6. [244]New Server Controls + 5.7. [245]Timeouts during REMOTE HOST Command Execution + (6) [246]INTERNATIONAL CHARACTER SETS + 6.0. [247]ISO 8859-15 Latin Alphabet 9 + 6.1. [248]The HP-Roman8 Character Set + 6.2. [249]Greek Character Sets + 6.3. [250]Additional Latin-2 Character Sets + 6.4. [251]Additional Cyrillic Character Sets + 6.5. [252]Automatic Character-Set Switching + 6.6. [253]Unicode + 6.6.1. [254]Overview of Unicode + 6.6.2. [255]UCS Byte Order + 6.6.2. [256]UCS Transformation Formats + 6.6.3. [257]Conformance Levels + 6.6.4. [258]Relationship of Unicode with Kermit's Other Character Sets + 6.6.5. [259]Kermit's Unicode Features + 6.6.5.1. [260]File Transfer + 6.6.5.2. [261]The TRANSLATE Command + 6.6.5.3. [262]Terminal Connection + 6.6.5.4. [263]The TRANSMIT Command + 6.6.5.5. [264]Summary of Kermit Unicode Commands + 6.7. [265]Client/Server Character-Set Switching + (7) [266]SCRIPT PROGRAMMING + 7.0. [267]Bug Fixes + 7.1. [268]The INPUT Command + 7.1.1. [269]INPUT Timeouts + 7.1.2. [270]New INPUT Controls + 7.1.3. [271]INPUT with Pattern Matching + 7.1.4. [272]The INPUT Match Result + 7.2. [273]New or Improved Built-In Variables + 7.3. [274]New or Improved Built-In Functions + 7.4. [275]New IF Conditions + 7.5. [276]Using More than Ten Macro Arguments + 7.6. [277]Clarification of Function Call Syntax + 7.7. [278]Autodownload during INPUT Command Execution + 7.8. [279]Built-in Help for Functions. + 7.9. [280]Variable Assignments + 7.9.1. [281]Assignment Operators + 7.9.2. [282]New Assignment Commands + 7.10. [283]Arrays + 7.10.1. [284]Array Initializers + 7.10.2. [285]Turning a String into an Array of Words + 7.10.3. [286]Arrays of Filenames + 7.10.4. [287]Automatic Arrays + 7.10.5. [288]Sorting Arrays + 7.10.6. [289]Displaying Arrays + 7.10.7. [290]Other Array Operations + 7.10.8. [291]Hints for Using Arrays + 7.10.9. [292]Do-It-Yourself Arrays + 7.10.10. [293]Associative Arrays + 7.11. [294]OUTPUT Command Improvements + 7.12. [295]Function and Variable Diagnostics + 7.13. [296]Return Value of Macros + 7.14. [297]The ASSERT, FAIL, and SUCCEED Commands. + 7.15. [298]Using Alarms + 7.16. [299]Passing Arguments to Command Files + 7.17. [300]Dialogs with Timed Responses + 7.18. [301]Increased Flexibility of SWITCH Case Labels + 7.19. "[302]Kerbang" Scripts + 7.20. [303]IF and XIF Statement Syntax + 7.20.1. [304]The IF/XIF Distinction + 7.20.2. [305]Boolean Expressions (The IF/WHILE Condition) + 7.21. [306]Screen Formatting and Cursor Control + 7.22. [307]Evaluating Arithmetic Expressions + 7.23. [308]Floating-Point Arithmetic + 7.24. [309]Tracing Script Execution + 7.25. [310]Compact Substring Notation + 7.26. [311]New WAIT Command Options + 7.26.1. [312]Waiting for Modem Signals + 7.26.2. [313]Waiting for File Events + 7.27. [314]Relaxed FOR and SWITCH Syntax + (8) [315]USING OTHER FILE TRANSFER PROTOCOLS + (9) [316]COMMAND-LINE OPTIONS + 9.0. [317]Extended-Format Command-Line Options + 9.1. [318]Command Line Personalities + 9.2. [319]Built-in Help for Command Line Options + 9.3. [320]New Command-Line Options + (10) [321]C-KERMIT AND G-KERMIT + +III. [322]APPENDICES + +III.1. [323]Character Set Tables +III.1.1. [324]The Hewlett Packard Roman8 Character Set +III.1.2. [325]Greek Character Sets +III.1.2.1. [326]The ISO 8859-7 Latin / Greek Alphabet +III.1.2.2. [327]The ELOT 927 Character Set +III.1.2.3. [328]PC Code Page 869 +III.2. [329]Updated Country Codes + +IV. [330]ERRATA & CORRIGENDA: Corrections to "Using C-Kermit" 2nd Edition. +V. [331]ADDITIONAL COPYRIGHT NOTICES + _________________________________________________________________ + +I. C-KERMIT DOCUMENTATION + + The user manual for C-Kermit is: + + Frank da Cruz and Christine M. Gianone, [332]Using C-Kermit, Second + Edition, Digital Press / Butterworth-Heinemann, Woburn, MA, 1997, + 622 pages, ISBN 1-55558-164-1. + + [333]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 / + [334]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 [335]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 [336]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, [337]Kermit News. Subscriptions are free; + contact Columbia University at the address above. + + Online news about Kermit is published in the + [338]comp.protocols.kermit.announce and + [339]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 [340]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 [341]Section 4, especially [342]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 [343]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 + [344]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 [345]Sections 1.5 and [346]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 [347]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 [348]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 [349]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 [350]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 [351]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 [352]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 ([353]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 [354]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 [355]\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. + [356]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 + [357]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(existatus) 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 + + [358]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 [359]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 [360]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 [361]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 + ([362]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 + [363]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 [364]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 ([365]Section 4.9) + * SWITCH case labels ([366]Section 7.18) + * The new IF MATCH statement ([367]Section 7.4) + * TYPE /MATCH ([368]Section 1.15) + * SET FILE TEXT-PATTERNS and BINARY-PATTERNS ([369]Section 4.3) + * The \fsearch() and \farraylook() functions ([370]Sections 7.3 and + [371]7.10.7) + * The \fpattern() function used with [M,RE]INPUT ([372]Section 7.1) + + Patterns are also called wildcards, especially when used for filename + matching. C-Kermit's pattern syntax is explained in [373]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 [374]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 [375]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 [376]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 [377]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 [378]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 [379]Section + 1.22.5. For instructions on handling function errors, see [380]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 [381]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 [382]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 ([383]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 [384]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: [385]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 [386]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 [387]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 + ([388]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 + ([389]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 occured. + 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 ([390]Section 2.1.13). + + \v(d$px) + PBX Exchange (see [391]Section 2.1.12). + + Other dial-related variables, already documented in [392]Using + C-Kermit (or other sections of this document, e.g. [393]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 [394]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 [395]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 [396]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 ([397]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 [398]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 [399]TELNET.TXT file, + also available on the Web in [400]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 [401]TELNET.TXT or [402]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 ([403]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 ([404]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 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 [405]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 + [406]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 [407]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 [408]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 + + 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 ([409]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: + + [410]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; [411]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: + + [412]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 [413]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 [414]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 [415]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 [416]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 [417]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 [418]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 [419]ckuins.txt and [420]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 [421]ckuins.txt + Section 15 and [422]Section 4.2 of the [423]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 [424]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.xyxcorp.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 [425]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 [426]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 ([427]Section 3.2) effective for + this CONNECT command only, temporarily overriding any current + SET TERMINAL TRIGGER values ([428]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 [429]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 [430]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 [431]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 + [432]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 ([433]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 [434]Sections 4.7.1 through + [435]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 [436]Sections 4.3 , [437]4.10, + [438]4.11, and [439]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 [440]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 [441]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, [442]Section 4.2.3) is in effect, or if a NOPUSH + command ([443]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 + [444]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 [445]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 + [446]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 + [447]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 [448]Sections 4.3, [449]4.10, [450]4.11, and [451]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: + [452]http://www.telstra.com.au/docs/PGP/ + Especially the topic "Using PGP as a UNIX-Style Filter": + [453]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 ([454]CLICK HERE for + details) and the new ability to run C-Kermit "though" other + communication programs, described in [455]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 [456]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 [457]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 ([458]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) writeability of the + current directory, but also because using them could result in wiping + out an existing file. See [459]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 [460]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 [461]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 transfered. 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 [462]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 [463]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 [464]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 ([465]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 ([466]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 + "\%*" ([467]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 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 ([468]Section 4.9). + + Optional switches include the standard file-selection switches + presented in [469]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 ([470]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 [471]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 + ([472]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 [473]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 + [474]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 [475]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 [476]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 [477]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 [478]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 ([479]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 ([480]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 beging + 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 + [481]Section 4.11 . + + /EXCEPT:pattern + See [482]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 + [483]Section 4.9.1). Including this switch is equivalent to + giving SET SEND BACKUP OFF ([484]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 [485]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 + [486]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 [487]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 [488]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 [489]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 ([490]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 [491]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 [492]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 [493]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 ([494]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 [495]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 [496]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 [497]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 [498]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 [499]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 [500]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 [501]Section 4.9.1 by loading a file list into an array + (e.g. with \ffiles(*,&a), see [502]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 [503]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 ([504]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 + [505]Sections 4.5.1 and [506]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 [507]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 [508]Section 7.10.3 and [509]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 [510]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 + ([511]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 ([512]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 [513]Sections 4.3, [514]4.10, and [515]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 ([516]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 ([517]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 [518]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 ([519]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 [520]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 ([521]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 + ([522]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 [523]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 [524]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 [525]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 [526]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 ([527]Section + 4.22.3 ), and fixed by: + + SET PREFIXING ALL + + It can also have numerous other causes, explained in Chapter 10 of + [528]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 work (UNIX "make", + backups, etc). + + Timezone handling is deferred for a future release. + _________________________________________________________________ + + 4.25. Autodownload Improvements + + Refer to pages 164-165 of [529]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 [530]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 + [531]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 ([532]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 [533]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 [534]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 [535]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 [536]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, Belorussian, 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 [537]RFC 1489. + + KOI8U + A similar extension of Old KOI-8, but for Ukrainian. It is + specified in [538]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: + + [539]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 [540]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 [541]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 ([542]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 [543]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 + [544]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 ([545]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 ([546]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 [547]Using C-Kermit and [548]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 ([549]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 fi +le + 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 [550]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 [551]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 [552]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 [553]Sections 1.19 and [554]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 [555]Sections 4.1 and [556]4.2. + + \v(filenumber) + Described in [557]Sections 4.1 and [558]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 [559]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 ([560]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 + [561]\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 [562]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 [563]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 [564]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 [565]Section 7.23) + + \v(math_e) + The value of e (see [566]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 [567]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 + [568]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 + [569]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) ([570]Section 7.24). + + \:(name[n:m]) + Equivalent to \fsubstring(name,n,m) (where "name" is any + \-quantity) ([571]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 + [572]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 nis + 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. + + \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 [573]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 nis 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 [574]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 [575]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 + [576]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 [577]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 [578]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 + [579]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 [580]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 + [581]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 [582]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 [583]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 [584]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 writeable, 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 [585]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 ([586]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 [587]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 [588]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 [589]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 + [590]Section 7.3 for details. + _________________________________________________________________ + + 7.10.3. Arrays of Filenames + + See [591]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 ([592]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 [593]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 [594]Section 7.10.10. + + See [595]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 [596]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 [597]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 [598]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 ([599]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. + + 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 [600]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 [601]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 nto 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 ([602]Section + 7.10.3). + * Assignment by \fsplit() starts with element 1 ([603]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 [604]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 [605]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 [606]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 ([607]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 ([608]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 + [609]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 [610]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 [611]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 ([612]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 [613]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 ( [614]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 ([615]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 ([616]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 ([617]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 [618]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 [619]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 desireable 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 [620]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 condition { + command1 + command2 + } ELSE IF condition { + 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.) + _________________________________________________________________ + + 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. + + Constructions of arbitrary complexity are possible, within reason. + + Also see [621]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 + + 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 [622]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 + [623]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 [624]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 [625]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 [626]Section 1.5; the use of + protocol-selection switches is described in [627]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 + [628]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 [629]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 [630]Sections 4.10 and [631]4.11 about + recursive file transfer. + + -V + Equivalent to SET FILE PATTERNS OFF ([632]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: + + [633]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 ([634]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 + [635]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 [636]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 [637]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: + + [638]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 forbbidden. 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 distrubution 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). + + [ [639]Top ] [ [640]C-Kermit ] [ [641]Kermit Home ] + _________________________________________________________________ + + CKERMIT70.HTM / The Kermit Project / Columbia University / 8 Feb 2000 + +References + + 1. http://www.columbia.edu/kermit/ckermit70.html#contents + 2. http://www.columbia.edu/kermit/ckermit.html + 3. http://www.columbia.edu/kermit/index.htm + 4. mailto:kermit-support@columbia.edu + 5. http://www.columbia.edu/kermit/ + 6. http://www.kermit-project.org/ + 7. http://www.columbia.nyc.ny.us/kermit/ + 8. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT + 9. ftp://kermit.columbia.edu/kermit/f/ckcmai.c + 10. http://www.columbia.edu/kermit/ckermit70.html#xv + 11. http://www.columbia.edu/kermit/ckb2.htm + 12. ftp://kermit.columbia.edu/kermit/f/ckcbwr.txt + 13. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt + 14. ftp://kermit.columbia.edu/kermit/f/ckvbwr.txt + 15. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt + 16. ftp://kermit.columbia.edu/kermit/f/ckermit70.txt + 17. ftp://kermit.columbia.edu/kermit/f/security.txt + 18. http://www.columbia.edu/kermit/security.htm + 19. ftp://kermit.columbia.edu/kermit/f/iksd.txt + 20. http://www.columbia.edu/kermit/iksd.htm + 21. http://www.columbia.edu/kermit/cuiksd.htm + 22. ftp://kermit.columbia.edu/kermit/f/telnet.txt + 23. http://www.columbia.edu/kermit/telnet.htm + 24. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT + 25. http://www.columbia.edu/kermit/k95.html + 26. http://www.opensource.org/ + 27. http://www.columbia.edu/kermit/ckb2.htm + 28. http://www.columbia.edu/kermit/ckermit70.html#xi + 29. http://www.columbia.edu/kermit/ckermit70.html#xii + 30. http://www.columbia.edu/kermit/ckermit70.html#x0 + 31. http://www.columbia.edu/kermit/ckermit70.html#x1 + 32. http://www.columbia.edu/kermit/ckermit70.html#x1.0 + 33. http://www.columbia.edu/kermit/ckermit70.html#x1.1 + 34. http://www.columbia.edu/kermit/ckermit70.html#x1.2 + 35. http://www.columbia.edu/kermit/ckermit70.html#x1.3 + 36. http://www.columbia.edu/kermit/ckermit70.html#x1.4 + 37. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 38. http://www.columbia.edu/kermit/ckermit70.html#x1.5.1 + 39. http://www.columbia.edu/kermit/ckermit70.html#x1.5.2 + 40. http://www.columbia.edu/kermit/ckermit70.html#x1.5.3 + 41. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 + 42. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 + 43. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 44. http://www.columbia.edu/kermit/ckermit70.html#x1.7 + 45. http://www.columbia.edu/kermit/ckermit70.html#x1.8 + 46. http://www.columbia.edu/kermit/ckermit70.html#x1.9 + 47. http://www.columbia.edu/kermit/ckermit70.html#x1.10 + 48. http://www.columbia.edu/kermit/ckermit70.html#x1.11 + 49. http://www.columbia.edu/kermit/ckermit70.html#x1.11.1 + 50. http://www.columbia.edu/kermit/ckermit70.html#x1.11.2 + 51. http://www.columbia.edu/kermit/ckermit70.html#x1.11.3 + 52. http://www.columbia.edu/kermit/ckermit70.html#x1.11.4 + 53. http://www.columbia.edu/kermit/ckermit70.html#x1.11.5 + 54. http://www.columbia.edu/kermit/ckermit70.html#x1.11.6 + 55. http://www.columbia.edu/kermit/ckermit70.html#x1.11.7 + 56. http://www.columbia.edu/kermit/ckermit70.html#x1.12 + 57. http://www.columbia.edu/kermit/ckermit70.html#x1.13 + 58. http://www.columbia.edu/kermit/ckermit70.html#x1.14 + 59. http://www.columbia.edu/kermit/ckermit70.html#x1.15 + 60. http://www.columbia.edu/kermit/ckermit70.html#x1.16 + 61. http://www.columbia.edu/kermit/ckermit70.html#x1.17 + 62. http://www.columbia.edu/kermit/ckermit70.html#x1.18 + 63. http://www.columbia.edu/kermit/ckermit70.html#x1.19 + 64. http://www.columbia.edu/kermit/ckermit70.html#x1.20 + 65. http://www.columbia.edu/kermit/ckermit70.html#x1.21 + 66. http://www.columbia.edu/kermit/ckermit70.html#x1.22 + 67. http://www.columbia.edu/kermit/ckermit70.html#x1.22.1 + 68. http://www.columbia.edu/kermit/ckermit70.html#x1.22.2 + 69. http://www.columbia.edu/kermit/ckermit70.html#x1.22.3 + 70. http://www.columbia.edu/kermit/ckermit70.html#x1.22.4 + 71. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 + 72. http://www.columbia.edu/kermit/ckermit70.html#x1.22.6 + 73. http://www.columbia.edu/kermit/ckermit70.html#x1.22.7 + 74. http://www.columbia.edu/kermit/ckermit70.html#x1.22.8 + 75. http://www.columbia.edu/kermit/ckermit70.html#x1.23 + 76. http://www.columbia.edu/kermit/ckermit70.html#x1.24 + 77. http://www.columbia.edu/kermit/ckermit70.html#x2 + 78. http://www.columbia.edu/kermit/ckermit70.html#x2.0 + 79. http://www.columbia.edu/kermit/ckermit70.html#x2.1 + 80. http://www.columbia.edu/kermit/ckermit70.html#x2.1.1 + 81. http://www.columbia.edu/kermit/ckermit70.html#x2.1.2 + 82. http://www.columbia.edu/kermit/ckermit70.html#x2.1.3 + 83. http://www.columbia.edu/kermit/ckermit70.html#x2.1.4 + 84. http://www.columbia.edu/kermit/ckermit70.html#x2.1.5 + 85. http://www.columbia.edu/kermit/ckermit70.html#x2.1.6 + 86. http://www.columbia.edu/kermit/ckermit70.html#x2.1.7 + 87. http://www.columbia.edu/kermit/ckermit70.html#x2.1.8 + 88. http://www.columbia.edu/kermit/ckermit70.html#x2.1.9 + 89. http://www.columbia.edu/kermit/ckermit70.html#x2.1.10 + 90. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 + 91. http://www.columbia.edu/kermit/ckermit70.html#x2.1.12 + 92. http://www.columbia.edu/kermit/ckermit70.html#x2.1.13 + 93. http://www.columbia.edu/kermit/ckermit70.html#x2.1.14 + 94. http://www.columbia.edu/kermit/ckermit70.html#x2.1.15 + 95. http://www.columbia.edu/kermit/ckermit70.html#x2.1.16 + 96. http://www.columbia.edu/kermit/ckermit70.html#x2.2 + 97. http://www.columbia.edu/kermit/ckermit70.html#x2.2.1 + 98. http://www.columbia.edu/kermit/ckermit70.html#x2.2.2 + 99. http://www.columbia.edu/kermit/ckermit70.html#x2.3 + 100. http://www.columbia.edu/kermit/ckermit70.html#x2.3.0 + 101. http://www.columbia.edu/kermit/ckermit70.html#x2.3.1 + 102. http://www.columbia.edu/kermit/ckermit70.html#x2.3.2 + 103. http://www.columbia.edu/kermit/ckermit70.html#x2.3.3 + 104. http://www.columbia.edu/kermit/ckermit70.html#x2.3.4 + 105. http://www.columbia.edu/kermit/ckermit70.html#x2.3.5 + 106. http://www.columbia.edu/kermit/ckermit70.html#x2.3.6 + 107. http://www.columbia.edu/kermit/ckermit70.html#x2.4 + 108. http://www.columbia.edu/kermit/ckermit70.html#x2.5 + 109. http://www.columbia.edu/kermit/ckermit70.html#x2.6 + 110. http://www.columbia.edu/kermit/ckermit70.html#x2.7 + 111. http://www.columbia.edu/kermit/ckermit70.html#x2.7.0 + 112. http://www.columbia.edu/kermit/ckermit70.html#x2.7.1 + 113. http://www.columbia.edu/kermit/ckermit70.html#x2.7.2 + 114. http://www.columbia.edu/kermit/ckermit70.html#x2.7.3 + 115. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4 + 116. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.1 + 117. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.2 + 118. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.3 + 119. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.4 + 120. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.5 + 121. http://www.columbia.edu/kermit/ckermit70.html#x2.8 + 122. http://www.columbia.edu/kermit/ckermit70.html#x2.9 + 123. http://www.columbia.edu/kermit/ckermit70.html#x2.9.1 + 124. http://www.columbia.edu/kermit/ckermit70.html#x2.9.2 + 125. http://www.columbia.edu/kermit/ckermit70.html#x2.10 + 126. http://www.columbia.edu/kermit/ckermit70.html#x2.11 + 127. http://www.columbia.edu/kermit/ckermit70.html#x2.12 + 128. http://www.columbia.edu/kermit/ckermit70.html#x2.13 + 129. http://www.columbia.edu/kermit/ckermit70.html#x2.14 + 130. http://www.columbia.edu/kermit/ckermit70.html#x2.15 + 131. http://www.columbia.edu/kermit/ckermit70.html#x3 + 132. http://www.columbia.edu/kermit/ckermit70.html#x3.1 + 133. http://www.columbia.edu/kermit/ckermit70.html#x3.2 + 134. http://www.columbia.edu/kermit/ckermit70.html#x3.3 + 135. http://www.columbia.edu/kermit/ckermit70.html#x3.4 + 136. http://www.columbia.edu/kermit/ckermit70.html#x4 + 137. http://www.columbia.edu/kermit/ckermit70.html#x4.0 + 138. http://www.columbia.edu/kermit/ckermit70.html#x4.1 + 139. http://www.columbia.edu/kermit/ckermit70.html#x4.1.1 + 140. http://www.columbia.edu/kermit/ckermit70.html#x4.1.2 + 141. http://www.columbia.edu/kermit/ckermit70.html#x4.1.3 + 142. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 143. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1 + 144. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.1 + 145. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.2 + 146. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.3 + 147. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 + 148. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2.1 + 149. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2.2 + 150. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 + 151. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3.1 + 152. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3.2 + 153. http://www.columbia.edu/kermit/ckermit70.html#x4.2.4 + 154. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 + 155. http://www.columbia.edu/kermit/ckermit70.html#x4.2.6 + 156. http://www.columbia.edu/kermit/ckermit70.html#x4.2.7 + 157. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8 + 158. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.1 + 159. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.2 + 160. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.3 + 161. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.4 + 162. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 163. http://www.columbia.edu/kermit/ckermit70.html#x4.3.1 + 164. http://www.columbia.edu/kermit/ckermit70.html#x4.3.2 + 165. http://www.columbia.edu/kermit/ckermit70.html#x4.3.3 + 166. http://www.columbia.edu/kermit/ckermit70.html#x4.3.4 + 167. http://www.columbia.edu/kermit/ckermit70.html#x4.4 + 168. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1 + 169. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1.1 + 170. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1.2 + 171. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2 + 172. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1 + 173. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1.1 + 174. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1.2 + 175. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.2 + 176. http://www.columbia.edu/kermit/ckermit70.html#x4.5 + 177. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 + 178. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2 + 179. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2.1 + 180. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2.2 + 181. http://www.columbia.edu/kermit/ckermit70.html#x4.5.3 + 182. http://www.columbia.edu/kermit/ckermit70.html#x4.5.4 + 183. http://www.columbia.edu/kermit/ckermit70.html#x4.6 + 184. http://www.columbia.edu/kermit/ckermit70.html#x4.7 + 185. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 186. http://www.columbia.edu/kermit/ckermit70.html#x4.7.2 + 187. http://www.columbia.edu/kermit/ckermit70.html#x4.7.3 + 188. http://www.columbia.edu/kermit/ckermit70.html#x4.8 + 189. http://www.columbia.edu/kermit/ckermit70.html#x4.8.1 + 190. http://www.columbia.edu/kermit/ckermit70.html#x4.8.2 + 191. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 192. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 193. http://www.columbia.edu/kermit/ckermit70.html#x4.9.2 + 194. http://www.columbia.edu/kermit/ckermit70.html#x4.9.3 + 195. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 196. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 197. http://www.columbia.edu/kermit/ckermit70.html#x4.11.1 + 198. http://www.columbia.edu/kermit/ckermit70.html#x4.11.2 + 199. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 200. http://www.columbia.edu/kermit/ckermit70.html#x4.11.4 + 201. http://www.columbia.edu/kermit/ckermit70.html#x4.11.5 + 202. http://www.columbia.edu/kermit/ckermit70.html#x4.11.6 + 203. http://www.columbia.edu/kermit/ckermit70.html#x4.12 + 204. http://www.columbia.edu/kermit/ckermit70.html#x4.13 + 205. http://www.columbia.edu/kermit/ckermit70.html#x4.14 + 206. http://www.columbia.edu/kermit/ckermit70.html#x4.15 + 207. http://www.columbia.edu/kermit/ckermit70.html#x4.16 + 208. http://www.columbia.edu/kermit/ckermit70.html#x4.17 + 209. http://www.columbia.edu/kermit/ckermit70.html#x4.17.1 + 210. http://www.columbia.edu/kermit/ckermit70.html#x4.17.2 + 211. http://www.columbia.edu/kermit/ckermit70.html#x4.18 + 212. http://www.columbia.edu/kermit/ckermit70.html#x4.19 + 213. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 214. http://www.columbia.edu/kermit/ckermit70.html#x4.20.1 + 215. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2 + 216. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.1 + 217. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.2 + 218. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.3 + 219. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.4 + 220. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.5 + 221. http://www.columbia.edu/kermit/ckermit70.html#x4.20.3 + 222. http://www.columbia.edu/kermit/ckermit70.html#x4.21 + 223. http://www.columbia.edu/kermit/ckermit70.html#x4.22 + 224. http://www.columbia.edu/kermit/ckermit70.html#x4.22.1 + 225. http://www.columbia.edu/kermit/ckermit70.html#x4.22.2 + 226. http://www.columbia.edu/kermit/ckermit70.html#x4.22.3 + 227. http://www.columbia.edu/kermit/ckermit70.html#x4.22.4 + 228. http://www.columbia.edu/kermit/ckermit70.html#x4.22.5 + 229. http://www.columbia.edu/kermit/ckermit70.html#x4.22.6 + 230. http://www.columbia.edu/kermit/ckermit70.html#x4.22.7 + 231. http://www.columbia.edu/kermit/ckermit70.html#x4.22.8 + 232. http://www.columbia.edu/kermit/ckermit70.html#x4.23 + 233. http://www.columbia.edu/kermit/ckermit70.html#x4.24 + 234. http://www.columbia.edu/kermit/ckermit70.html#x4.25 + 235. http://www.columbia.edu/kermit/ckermit70.html#x5 + 236. http://www.columbia.edu/kermit/ckermit70.html#x5.0 + 237. http://www.columbia.edu/kermit/ckermit70.html#x5.1 + 238. http://www.columbia.edu/kermit/ckermit70.html#x5.2 + 239. http://www.columbia.edu/kermit/ckermit70.html#x5.3 + 240. http://www.columbia.edu/kermit/ckermit70.html#x5.3.1 + 241. http://www.columbia.edu/kermit/ckermit70.html#x5.3.2 + 242. http://www.columbia.edu/kermit/ckermit70.html#x5.4 + 243. http://www.columbia.edu/kermit/ckermit70.html#x5.5 + 244. http://www.columbia.edu/kermit/ckermit70.html#x5.6 + 245. http://www.columbia.edu/kermit/ckermit70.html#x5.7 + 246. http://www.columbia.edu/kermit/ckermit70.html#x6 + 247. http://www.columbia.edu/kermit/ckermit70.html#x6.0 + 248. http://www.columbia.edu/kermit/ckermit70.html#x6.1 + 249. http://www.columbia.edu/kermit/ckermit70.html#x6.2 + 250. http://www.columbia.edu/kermit/ckermit70.html#x6.3 + 251. http://www.columbia.edu/kermit/ckermit70.html#x6.4 + 252. http://www.columbia.edu/kermit/ckermit70.html#x6.5 + 253. http://www.columbia.edu/kermit/ckermit70.html#x6.6 + 254. http://www.columbia.edu/kermit/ckermit70.html#x6.6.1 + 255. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 + 256. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 + 257. http://www.columbia.edu/kermit/ckermit70.html#x6.6.3 + 258. http://www.columbia.edu/kermit/ckermit70.html#x6.6.4 + 259. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5 + 260. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.1 + 261. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.2 + 262. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.3 + 263. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.4 + 264. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.5 + 265. http://www.columbia.edu/kermit/ckermit70.html#x6.7 + 266. http://www.columbia.edu/kermit/ckermit70.html#x7 + 267. http://www.columbia.edu/kermit/ckermit70.html#x7.0 + 268. http://www.columbia.edu/kermit/ckermit70.html#x7.1 + 269. http://www.columbia.edu/kermit/ckermit70.html#x7.1.1 + 270. http://www.columbia.edu/kermit/ckermit70.html#x7.1.2 + 271. http://www.columbia.edu/kermit/ckermit70.html#x7.1.3 + 272. http://www.columbia.edu/kermit/ckermit70.html#x7.1.4 + 273. http://www.columbia.edu/kermit/ckermit70.html#x7.2 + 274. http://www.columbia.edu/kermit/ckermit70.html#x7.3 + 275. http://www.columbia.edu/kermit/ckermit70.html#x7.4 + 276. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 277. http://www.columbia.edu/kermit/ckermit70.html#x7.6 + 278. http://www.columbia.edu/kermit/ckermit70.html#x7.7 + 279. http://www.columbia.edu/kermit/ckermit70.html#x7.8 + 280. http://www.columbia.edu/kermit/ckermit70.html#x7.9 + 281. http://www.columbia.edu/kermit/ckermit70.html#x7.9.1 + 282. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 + 283. http://www.columbia.edu/kermit/ckermit70.html#x7.10 + 284. http://www.columbia.edu/kermit/ckermit70.html#x7.10.1 + 285. http://www.columbia.edu/kermit/ckermit70.html#x7.10.2 + 286. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 + 287. http://www.columbia.edu/kermit/ckermit70.html#x7.10.4 + 288. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 + 289. http://www.columbia.edu/kermit/ckermit70.html#x7.10.6 + 290. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 291. http://www.columbia.edu/kermit/ckermit70.html#x7.10.8 + 292. http://www.columbia.edu/kermit/ckermit70.html#x7.10.9 + 293. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10 + 294. http://www.columbia.edu/kermit/ckermit70.html#x7.11 + 295. http://www.columbia.edu/kermit/ckermit70.html#x7.12 + 296. http://www.columbia.edu/kermit/ckermit70.html#x7.13 + 297. http://www.columbia.edu/kermit/ckermit70.html#x7.14 + 298. http://www.columbia.edu/kermit/ckermit70.html#x7.15 + 299. http://www.columbia.edu/kermit/ckermit70.html#x7.16 + 300. http://www.columbia.edu/kermit/ckermit70.html#x7.17 + 301. http://www.columbia.edu/kermit/ckermit70.html#x7.18 + 302. http://www.columbia.edu/kermit/ckermit70.html#x7.19 + 303. http://www.columbia.edu/kermit/ckermit70.html#x7.20 + 304. http://www.columbia.edu/kermit/ckermit70.html#x7.20.1 + 305. http://www.columbia.edu/kermit/ckermit70.html#x7.20.2 + 306. http://www.columbia.edu/kermit/ckermit70.html#x7.21 + 307. http://www.columbia.edu/kermit/ckermit70.html#x7.22 + 308. http://www.columbia.edu/kermit/ckermit70.html#x7.23 + 309. http://www.columbia.edu/kermit/ckermit70.html#x7.24 + 310. http://www.columbia.edu/kermit/ckermit70.html#x7.25 + 311. http://www.columbia.edu/kermit/ckermit70.html#x7.26 + 312. http://www.columbia.edu/kermit/ckermit70.html#x7.26.1 + 313. http://www.columbia.edu/kermit/ckermit70.html#x7.26.2 + 314. http://www.columbia.edu/kermit/ckermit70.html#x7.27 + 315. http://www.columbia.edu/kermit/ckermit70.html#x8 + 316. http://www.columbia.edu/kermit/ckermit70.html#x9 + 317. http://www.columbia.edu/kermit/ckermit70.html#x9.0 + 318. http://www.columbia.edu/kermit/ckermit70.html#x9.1 + 319. http://www.columbia.edu/kermit/ckermit70.html#x9.2 + 320. http://www.columbia.edu/kermit/ckermit70.html#x9.3 + 321. http://www.columbia.edu/kermit/ckermit70.html#x10 + 322. http://www.columbia.edu/kermit/ckermit70.html#xiii + 323. http://www.columbia.edu/kermit/ckermit70.html#xiii.1 + 324. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.1 + 325. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2 + 326. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.1 + 327. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.2 + 328. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.3 + 329. http://www.columbia.edu/kermit/ckermit70.html#xiii.2 + 330. http://www.columbia.edu/kermit/ckermit70.html#xiv + 331. http://www.columbia.edu/kermit/ckermit70.html#xv + 332. http://www.columbia.edu/kermit/ckb2.htm + 333. http://www.columbia.edu/kermit/ckbreviews.html + 334. http://www.bhusa.com/ + 335. http://www.columbia.edu/kermit/manuals.html#ckde + 336. http://www.columbia.edu/kermit/manuals.html#ktb + 337. http://www.columbia.edu/kermit/news.html + 338. news:comp.protocols.kermit.announce + 339. news:comp.protocols.kermit.misc + 340. http://www.columbia.edu/kermit/ckb2.htm + 341. http://www.columbia.edu/kermit/ckermit70.html#x4 + 342. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 343. http://www.columbia.edu/kermit/ckermit70.html#x4.23 + 344. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 + 345. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 346. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 347. http://www.columbia.edu/kermit/ckermit70.html#x4.9. + 348. http://www.columbia.edu/kermit/ckb2.htm + 349. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 + 350. http://www.columbia.edu/kermit/ckermit70.html#x2.15 + 351. http://www.columbia.edu/kermit/ckermit70.html#x9.1 + 352. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 353. http://www.columbia.edu/kermit/ckermit70.html#x7.4 + 354. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 355. http://www.columbia.edu/kermit/ckermit70.html#mjd + 356. http://www.columbia.edu/kermit/ckermit70.html#mjd + 357. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 358. http://www.columbia.edu/kermit/ckb2.htm + 359. http://www.columbia.edu/kermit/ckb2.htm + 360. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 361. http://www.columbia.edu/kermit/ckermit70.html#x2.12 + 362. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 363. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 364. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5 + 365. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 366. http://www.columbia.edu/kermit/ckermit70.html#x7.18 + 367. http://www.columbia.edu/kermit/ckermit70.html#x7.4 + 368. http://www.columbia.edu/kermit/ckermit70.html#x1.15 + 369. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 370. http://www.columbia.edu/kermit/ckermit70.html#x7.3 + 371. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 372. http://www.columbia.edu/kermit/ckermit70.html#x7.1 + 373. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 374. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt + 375. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt + 376. http://www.columbia.edu/kermit/ckermit70.html#x1.22.4 + 377. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 + 378. http://www.columbia.edu/kermit/ckb2.htm + 379. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 + 380. http://www.columbia.edu/kermit/ckermit70.html#x7.12 + 381. http://www.columbia.edu/kermit/ckermit70.html#x2.1.16 + 382. http://www.columbia.edu/kermit/ckermit70.html#x2.7 + 383. http://www.columbia.edu/kermit/ckermit70.html#x2.3.5 + 384. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 385. http://www.telefonica.es/cambiodenumeracion/ + 386. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 387. http://www.columbia.edu/kermit/ckb2.htm + 388. http://www.columbia.edu/kermit/ckermit70.html#x2.2.2 + 389. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 + 390. http://www.columbia.edu/kermit/ckermit70.html#x2.1.13 + 391. http://www.columbia.edu/kermit/ckermit70.html#x2.1.12 + 392. http://www.columbia.edu/kermit/ckb2.htm + 393. http://www.columbia.edu/kermit/ckermit70.html#x2.1.1 + 394. http://www.columbia.edu/kermit/ckb2.htm + 395. http://www.columbia.edu/kermit/ckb2.htm + 396. http://www.columbia.edu/kermit/ckermit70.html#x2.1.7 + 397. http://www.columbia.edu/kermit/ckermit70.html#x2.1.6 + 398. http://www.columbia.edu/kermit/ckb2.htm + 399. ftp://kermit.columbia.edu/kermit/f/telnet.txt + 400. http://www.columbia.edu/kermit/telnet.htm + 401. ftp://kermit.columbia.edu/kermit/f/telnet.txt + 402. http://www.columbia.edu/kermit/telnet.htm + 403. ftp://ftp.isi.edu/in-notes/rfc1572.txt + 404. ftp://ftp.isi.edu/in-notes/rfc779.txt + 405. http://www.columbia.edu/kermit/ckb2.htm + 406. http://www.columbia.edu/kermit/ckermit70.html#x2.10 + 407. http://www.columbia.edu/kermit/ckermit70.html#x2.8 + 408. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 409. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 410. http://www.psy.uq.oz.au/~ftp/Crypto/ + 411. http://www.columbia.edu/kermit/security.htm + 412. http://srp.stanford.edu/srp/ + 413. http://www.columbia.edu/kermit/ckermit70.html#x2.7.1, + 414. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt + 415. http://www.columbia.edu/kermit/security.htm + 416. http://www.columbia.edu/kermit/ckb2.htm + 417. http://www.columbia.edu/kermit/ckermit70.html#x2.7 + 418. http://www.columbia.edu/kermit/ckermit70.html#x2.0 + 419. ftp://kermit.columbia.edu/kermit/f/ckuins.txt + 420. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt + 421. ftp://kermit.columbia.edu/kermit/f/ckuins.txt + 422. http://www.columbia.edu/kermit/iksd.html#x4.2 + 423. http://www.columbia.edu/kermit/iksd.html + 424. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.1 + 425. ftp://ftp.isi.edu/in-notes/rfc1945.txt + 426. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 427. http://www.columbia.edu/kermit/ckermit70.html#x3.2 + 428. http://www.columbia.edu/kermit/ckermit70.html#x3.2 + 429. http://www.columbia.edu/kermit/ckb2.htm + 430. http://www.columbia.edu/kermit/ckb2.htm + 431. http://www.columbia.edu/kermit/ckermit70.html#x5.4 + 432. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt + 433. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 434. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 435. http://www.columbia.edu/kermit/ckermit70.html#x4.7.3 + 436. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 437. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 438. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 439. http://www.columbia.edu/kermit/ckermit70.html#x4.15 + 440. http://www.columbia.edu/kermit/ckermit70.html#x4.2.4 + 441. http://www.columbia.edu/kermit/ckermit70.html#x4.7 + 442. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 + 443. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.3 + 444. http://www.columbia.edu/kermit/ckb2.htm + 445. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 + 446. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 447. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.2 + 448. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 449. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 450. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 451. http://www.columbia.edu/kermit/ckermit70.html#x4.15 + 452. http://www.telstra.com.au/docs/PGP/ + 453. http://www.telstra.com.au/docs/PGP/pgpdoc2/pgpdoc2_17.html + 454. http://www.columbia.edu/kermit/security.htm + 455. http://www.columbia.edu/kermit/ckermit70.html#x2.7 + 456. http://www.columbia.edu/kermit/ckb2.htm + 457. http://www.columbia.edu/kermit/ckermit70.html#x2.14 + 458. http://www.columbia.edu/kermit/ckermit70.html#x1.23 + 459. http://www.columbia.edu/kermit/ckermit70.html#x4.7 + 460. http://www.columbia.edu/kermit/ckb2.htm + 461. http://www.columbia.edu/kermit/ckb2.htm + 462. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 463. http://www.columbia.edu/kermit/ckb2.htm + 464. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 + 465. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 466. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 + 467. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 468. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 469. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 + 470. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 471. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 + 472. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 + 473. http://www.columbia.edu/kermit/ckb2.htm + 474. http://www.columbia.edu/kermit/ckb2.htm + 475. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 476. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 477. http://www.columbia.edu/kermit/ckermit70.html#x7.10 + 478. http://www.columbia.edu/kermit/ckermit70.html#x7.10.11 + 479. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 480. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 + 481. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 482. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 + 483. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 484. http://www.columbia.edu/kermit/ckermit70.html#x4.0.6 + 485. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 486. http://www.columbia.edu/kermit/ckermit70.html#x4.1 + 487. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 488. http://www.columbia.edu/kermit/ckb2.htm + 489. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 490. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 + 491. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 492. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 493. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 494. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 + 495. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 496. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 497. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 498. http://www.columbia.edu/kermit/ckermit70.html#x1.11.5 + 499. http://www.columbia.edu/kermit/ckermit70.html#x4.0.6 + 500. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 501. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 502. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 503. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 504. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 505. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 + 506. http://www.columbia.edu/kermit/ckermit70.html#x7.10 + 507. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 + 508. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 + 509. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 + 510. http://www.columbia.edu/kermit/ckb2.htm + 511. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 512. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 513. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 514. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 515. http://www.columbia.edu/kermit/ckermit70.html#x4.15 + 516. http://www.columbia.edu/kermit/ckermit70.html#x4.18 + 517. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 518. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 519. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 520. http://www.columbia.edu/kermit/ckermit70.html#x4.19 + 521. http://www.columbia.edu/kermit/ckermit70.html#x4.16 + 522. http://www.columbia.edu/kermit/ckermit70.html#x4.19 + 523. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.3 + 524. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 525. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.4 + 526. http://www.columbia.edu/kermit/ckermit70.html#x4.22.2 + 527. http://www.columbia.edu/kermit/ckermit70.html#x4.22.3 + 528. http://www.columbia.edu/kermit/ckb2.htm + 529. http://www.columbia.edu/kermit/ckb2.htm + 530. http://www.columbia.edu/kermit/ckermit70.html#x9.3 + 531. http://www.columbia.edu/kermit/ckermit70.html#x5.2.1 + 532. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 + 533. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2 + 534. http://www.columbia.edu/kermit/ckermit70.html#x6.6 + 535. http://www.columbia.edu/kermit/ckermit70.html#xiii + 536. http://www.columbia.edu/kermit/ckermit70.html#xiii + 537. ftp://ftp.isi.edu/in-notes/rfc1489.txt + 538. ftp://ftp.isi.edu/in-notes/rfc2319.txt + 539. http://www.unicode.org/ + 540. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 + 541. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.1 + 542. ftp://ftp.isi.edu/in-notes/rfc2640.txt + 543. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 + 544. http://www.columbia.edu/kermit/ckermit70.html#x6.0 + 545. http://www.columbia.edu/kermit/ckermit70.html#x6.5 + 546. http://www.columbia.edu/kermit/ckermit70.html#x6.4 + 547. http://www.columbia.edu/kermit/ckb2.htm + 548. http://www.columbia.edu/kermit/ckermit70.html#x4.21 + 549. http://www.columbia.edu/kermit/ckermit70.html#x6.5 + 550. http://www.columbia.edu/kermit/ckermit70.html#x2.8 + 551. http://www.columbia.edu/kermit/ckermit70.html#x7.7 + 552. http://www.columbia.edu/kermit/ckermit70.html#x7.2 + 553. http://www.columbia.edu/kermit/ckermit70.html#x1.19 + 554. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 555. http://www.columbia.edu/kermit/ckermit70.html#x4.1 + 556. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 557. http://www.columbia.edu/kermit/ckermit70.html#x4.1 + 558. http://www.columbia.edu/kermit/ckermit70.html#x4.2 + 559. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 + 560. http://www.columbia.edu/kermit/ckermit70.html#x2.10 + 561. http://www.columbia.edu/kermit/ckermit70.html#ferrstring + 562. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 + 563. http://www.columbia.edu/kermit/ckermit70.html#x2.1.10 + 564. http://www.columbia.edu/kermit/ckermit70.html#x9.1 + 565. http://www.columbia.edu/kermit/ckermit70.html#x7.23 + 566. http://www.columbia.edu/kermit/ckermit70.html#x7.23 + 567. http://www.columbia.edu/kermit/ckermit70.html#x1.22 + 568. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 569. http://www.columbia.edu/kermit/ckermit70.html#x7.23 + 570. http://www.columbia.edu/kermit/ckermit70.html#x7.24 + 571. http://www.columbia.edu/kermit/ckermit70.html#x7.24 + 572. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 + 573. http://www.columbia.edu/kermit/ckermit70.html#x7.12 + 574. http://www.columbia.edu/kermit/ckermit70.html#x7.9 + 575. http://www.columbia.edu/kermit/ckb2.htm + 576. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 577. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 578. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 579. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 580. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 581. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.4 + 582. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 + 583. http://www.columbia.edu/kermit/ckermit70.html#x7.8 + 584. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 585. http://www.columbia.edu/kermit/ckb2.htm + 586. http://www.columbia.edu/kermit/ckermit70.html#x7.19 + 587. http://www.columbia.edu/kermit/ckermit70.html#x7.16 + 588. http://www.columbia.edu/kermit/ckermit70.html#x7.9.1 + 589. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 590. http://www.columbia.edu/kermit/ckermit70.html#x7.3 + 591. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 592. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 + 593. http://www.columbia.edu/kermit/ckermit70.html#x7.10 + 594. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10 + 595. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 596. http://www.columbia.edu/kermit/ckermit70.html#x7.23 + 597. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 598. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 + 599. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 + 600. http://www.columbia.edu/kermit/ckermit70.html#x7.10.11 + 601. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 602. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 + 603. http://www.columbia.edu/kermit/ckermit70.html#x7.3 + 604. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 + 605. http://www.columbia.edu/kermit/ckb2.htm + 606. http://www.columbia.edu/kermit/ckermit70.html#x4.17.2 + 607. http://www.columbia.edu/kermit/ckermit70.html#x1.22 + 608. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 609. http://www.columbia.edu/kermit/ckermit70.html#x1.22 + 610. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 611. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 612. http://www.columbia.edu/kermit/ckermit70.html#x1.22 + 613. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 614. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 615. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 616. http://www.columbia.edu/kermit/ckermit70.html#x7.19 + 617. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 + 618. http://www.columbia.edu/kermit/ckermit70.html#x9.3 + 619. http://www.columbia.edu/kermit/ckermit70.html#x7.5 + 620. http://www.columbia.edu/kermit/ckb2.htm + 621. http://www.columbia.edu/kermit/ckermit70.html#x7.4 + 622. http://www.columbia.edu/kermit/ckermit70.html#x7.20.2 + 623. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 + 624. http://www.columbia.edu/kermit/ckermit70.html#x7.10.9 + 625. http://www.columbia.edu/kermit/ckermit70.html#x1.10 + 626. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 627. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 + 628. http://www.columbia.edu/kermit/iksd.html + 629. http://www.columbia.edu/kermit/ckermit70.html#x7.19 + 630. http://www.columbia.edu/kermit/ckermit70.html#x4.10 + 631. http://www.columbia.edu/kermit/ckermit70.html#x4.11 + 632. http://www.columbia.edu/kermit/ckermit70.html#x4.3 + 633. http://www.columbia.edu/kermit/gkermit.html + 634. http://www.columbia.edu/kermit/ckermit70.html#x4.20 + 635. http://www.columbia.edu/kermit/gkermit.html + 636. http://www.columbia.edu/kermit/ckb2.htm + 637. http://www.columbia.edu/kermit/ckermit70.html#x7.3 + 638. mailto:kermit@columbia.edu + 639. http://www.columbia.edu/kermit/ckermit70.html#top + 640. http://www.columbia.edu/kermit/ckermit.html + 641. http://www.columbia.edu/kermit/index.html diff --git a/ckermit80.txt b/ckermit80.txt new file mode 100644 index 0000000..493338a --- /dev/null +++ b/ckermit80.txt @@ -0,0 +1,10349 @@ + + C-Kermit 8.0 Update Notes + + [ [1]Contents ] [ [2]C-Kermit ] [ [3]Kermit Home ] + + Second Supplement to Using C-Kermit, Second 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: Sat Apr 10 16:36:11 2004 + + * 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: + [4]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: [5]kermit-support@columbia.edu +Web: [6]http://www.columbia.edu/kermit/ +Or: [7]http://www.kermit-project.org/ +Or: [8]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 [9]COPYING.TXT file or the + copyright text in the [10]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 + [11]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 [12]Using C-Kermit; and: + * The [13]C-Kermit 7.0 Update Notes. Also available in plain-text + form as [14]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: + + [15]ckututor.html + C-Kermit Tutorial (for Unix). Also distributed in Nroff form as + [16]ckuker.nr, the Unix C-Kermit manual page. + + [17]security.htm + Discussion of Kermit's new authentication and encryption + features, updated for C-Kermit 8.0. + + [18]telnet.htm + Detailed documentation of Kermit's Telnet client, updated for + C-Kermit 8.0. + + [19]ftpscripts.html + Tutorial: Writing FTP automation scripts + + [20]ckcbwr.html + Platform-independent C-Kermit hints and tips. Also distributed + in plain text form as [21]ckcbwr.txt + + [22]ckubwr.html + Unix-specific C-Kermit hints and tips. Also distributed in + plain text form as [23]ckubwr.txt. + + [24]ckvbwr.html + VMS-specific C-Kermit hints and tips. Also distributed in plain + text form as [25]ckvbwr.txt. + + [26]ckuins.html + Unix C-Kermit installation instructions. Also distributed in + plain text form as [27]ckuins.txt. + + [28]ckvins.html + VMS C-Kermit installation instructions. Also distributed in + plain text form as [29]ckvins.txt. + + [30]ckccfg.html + Compile-time configuration options. Also distributed in plain + text form as [31]ckccfg.txt. + + [32]ckcplm.html + C-Kermit Program Logic Manual. Also distributed in plain text + form as [33]ckcplm.txt. + + [34]iksd.html + Internet Kermit Service Aministrators Guide for Unix. + + [35]skermit.html + C-Kermit as an SSH Subsystem (SFTP server replacement). + + [ [36]Top ] [ [37]C-Kermit ] [ [38]Kermit Home ] + __________________________________________________________________________ + +CONTENTS + + [39]0. WHAT'S NEW + [40]1. FIXES SINCE VERSION 7.0.196 + [41]2. SSH AND HTTP + [42]2.1. SSH Connections + [43]2.2. HTTP Connections + [44]2.2.1. HTTP Command Switches + [45]2.2.2. HTTP Action Commands + [46]2.2.3. HTTP Headers + [47]2.2.4. Secure HTTP Connections + [48]2.2.5. HTTP Variables + [49]2.2.6. The HTTP Command-Line Personality + [50]3. THE BUILT-IN FTP CLIENT + [51]3.1. Making and Managing FTP Connections + [52]3.1.1. Kermit Command-Line Options for FTP + [53]3.1.2. The FTP Command-Line Personality + [54]3.1.3. The FTP URL Interpreter + [55]3.1.4. Interactive FTP Session Establishment + [56]3.2. Making Secure FTP Connections + [57]3.3. Setting FTP Preferences + [58]3.4. Managing Directories and Files + [59]3.5. Uploading Files With FTP + [60]3.5.1. FTP PUT Switches + [61]3.5.2. Update Mode + [62]3.5.3. Recovery + [63]3.6. Downloading Files With FTP + [64]3.6.1. FTP GET Switches + [65]3.6.2. Filename Collisions + [66]3.6.3. Recovery + [67]3.7. Translating Character Sets + [68]3.7.1. Character Sets and Uploading + [69]3.7.2. Character Sets and Downloading + [70]3.8. FTP Command Shortcuts + [71]3.9. Dual Sessions + [72]3.10. Automating FTP Sessions + [73]3.10.1. FTP-Specific Variables and Functions + [74]3.10.2. Examples + [75]3.10.3. Automating Secure FTP Connections + [76]3.11. Advanced FTP Protocol Features [77]4. FILE SCANNING + [78]5. FILE AND DIRECTORY NAMES CONTAINING SPACES + [79]6. OTHER COMMAND PARSING IMPROVEMENTS + [80]6.1. Grouping Macro Arguments + [81]6.2. Directory and File Name Completion + [82]6.3. Passing Arguments to Command Files + [83]6.4. More-Prompting + [84]6.5. Commas in Macro Definitions + [85]6.6. Arrow Keys + [86]7. NEW COMMANDS AND SWITCHES + [87]8. SCRIPTING IMPROVEMENTS + [88]8.1. Performance and Debugging + [89]8.2. Using Macros as Numeric Variables + [90]8.3. New IF Conditions + [91]8.4. The ON_UNKNOWN_COMMAND and ON_CD Macros + [92]8.5. The SHOW MACRO Command + [93]8.6. Arrays + [94]8.7. New or Improved Built-in Variables and Functions + [95]8.8. The RETURN and END Commands + [96]8.9. UNDEFINing Groups of Variables + [97]8.10. The INPUT and MINPUT Commands + [98]8.11. Learned Scripts + [99]8.12. Pattern Matching + [100]8.13. Dates and Times + [101]8.14. Trapping Keyboard Interruption + [102]9. S-EXPRESSIONS + [103]9.1. What is an S-Expression? + [104]9.2. Integer and Floating-Point-Arithmetic + [105]9.3. How to Use S-Expressions + [106]9.4. Summary of Built-in Constants and Operators + [107]9.5. Variables + [108]9.6. Assignments and Scope + [109]9.7. Conditional Expressions + [110]9.8. Extensibility + [111]9.9. Examples + [112]9.10. Differences from Algebraic Notation + [113]9.11.Differences from Lisp + [114]10. FILE TRANSFER + [115]11. MODEMS AND DIALING + [116]12. TERMINAL CONNECTION + [117]13. CHARACTER SETS + [118]14. DIALOUT FROM TELNET TERMINAL SERVERS + [119]15. COPING WITH BROKEN KERMIT PARTNERS + [120]16. NEW COMMAND-LINE OPTIONS + [121]17. LOGS + + [ [122]Top ] [ [123]C-Kermit ] [ [124]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 [125]Section 5 of the Unix C-Kermit + [126]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 [127]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 [128]Section 2.2. + + C-Kermit 7.1 Alpha.01 (8 December 2000) + + Its major new features are those listed in the [129]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 [130]FTP, TELNET, and IKSD URLs as its + first command-line argument. + + Character-set translation added to the FTP client for + [131]filenames. + + Optional [132]setting of date of incoming files by FTP [M]GET + from the server date. + + [133]FTP CHECK filename added to let FTP client check the + existence of a file on the server. + + [134]FTP GET /NAMELIST:filename added to get list of server + filenames into a local file. + + [135]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 [136]VDIRECTORY added for getting verbose directory + listings from TOPS-20. + + [137]FTP TYPE TENEX added for transferring 8-bit binary files + with PDP-10s. + + Added [138]automatic text/binary mode switching for FTP + [M]GET, based on filename patterns (e.g. *.zip, *.gz, *.exe + are binary; *.txt, *.c are text). + + [139]SET SEND I-PACKETS OFF added for coping with Kermit + servers that do not support I packets. + + A new option was added to [140]\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 + surpressed. + 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 platorms. + + 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. + + [141]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) + + + [142]HTTP 1.1 connections and scripting + + [143]ON_CTRLC macro for trapping Ctrl-C in scripts + + [144]Date-time parsing improvements, timezones, comparison, + arithmetic + + [145]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) + + + [146]New Unix man page + + [147]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 Certficate 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 synomym for RS232-SIGNAL + + Support for secure URL protocols added: telnets:, ftps:, + https:. + + C-Kermit 8.0.201 (8 Feb 2002) + + + Installability as an [148]SSH v2 Subsystem. + + [149]SET LOCUS command. + + [150]L-versions of CD, DIR, DELETE, MKDIR, etc, to force + local execution. + + [151]USER and ACCOUNT added as synonyms for FTP USER and FTP + ACCOUNT. + + [152]SHOW VARIABLES now accepts a list of variables. + + Rudimentary support for [153]Caller ID when receiving phone + calls. + + Up/Down [154]Arrow-key navigation of command history buffer. + + [155]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 [156]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 ANONOMOUS-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. + + [ [157]Top ] [ [158]Contents ] [ [159]C-Kermit ] [ [160]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 [161]previous section. + + [ [162]Top ] [ [163]Contents ] [ [164]C-Kermit ] [ [165]Kermit Home ] + __________________________________________________________________________ + +2. SSH AND HTTP + + 2.1. SSH Connections + + This section does not apply to [166]Kermit 95 2.0, which has its + own built-in SSH client, which is documented [167]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; [168]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. + + [ [169]SSH Kermit Server Subsystem ] [ [170]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 ([171]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, [172]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 [173]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 ([174]Section 2.2.1). The optional URI is a "compact string + of characters for identifying an abstract or physical resource" + ([175]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 [176]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 [177]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 assurrance 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 [178]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-Modifed:" item is the file's modification date and time. An + example showing how to use this information is presented in + [179]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 [180]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 [181]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 certifcates, 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 ([182]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 [183]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 [184]Section 16, you can also give a URL instead + of a host name and options. + + [ [185]Top ] [ [186]Contents ] [ [187]C-Kermit Home ] [ [188]Kermit + Home ] + __________________________________________________________________________ + +3. THE BUILT-IN FTP CLIENT + + 3.1. [189]Making and Managing FTP Connections + 3.2. [190]Making Secure FTP Connections + 3.3. [191]Setting FTP Preferences + 3.4. [192]Managing Directories and Files + 3.5. [193]Uploading Files With FTP + 3.6. [194]Downloading Files With FTP + 3.7. [195]Translating Character Sets + 3.8. [196]FTP Command Shortcuts + 3.9. [197]Dual Sessions + 3.10. [198]Automating FTP Sessions + 3.11. [199]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 [200]security methods can be used to + establish and conduct secure FTP sessions with [201]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. + * [202]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, [203]Unicode UTF-8. This includes + both the file's name and (for text files only) its contents. + * All of C-Kermit's [204]file-selection mechanisms are available: + size, date, name patterns and lists, exception lists, etc. + * [205]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 [206]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 + ([207]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 [208]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 :-). + + [ [209]Top ] [ [210]FTP Top ] [ [211]FTP Client Overview ] [ [212]FTP + Script Tutorial ] [ [213]C-Kermit Home ] [ [214]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 [215]command-line options, + by [216]URL, or by [217]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 ([218]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 [219]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 + [220]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 [221]Sections + 3.5 and [222]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. [223]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 [224]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 + [225]SET FTP AUTOLOGIN and the [226]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 [227]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 + [228]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. + + [ [229]Top ] [ [230]FTP Top ] [ [231]C-Kermit Home ] [ [232]Kermit + Home ] + _________________________________________________________________ + + 3.2. Making Secure FTP Connections + + Also see: [233]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 [234]Section 3.10. + + [ [235]Top ] [ [236]FTP Top ] [ [237]C-Kermit Home ] [ [238]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 [239]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 [240]Section 4.17 of the [241]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 surpresses 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 [242]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 [243]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 [244]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 [245]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 [246]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 ([247]Section 3.7). + + SET FTP SERVER-TIME-OFFSET delta-time + Tells Kermit to apply the given [248]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 cancelling 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 [249]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 [250]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 [251]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 ([252]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). + + [ [253]Top ] [ [254]FTP Top ] [ [255]C-Kermit Home ] [ [256]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 + [257]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 [258]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 + [259]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 ([260]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 [261]\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 [262]\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 decriptions of these + switches in [263]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 [264]SET FTP TYPE for details. + + [ [265]Top ] [ [266]FTP Top ] [ [267]C-Kermit Home ] [ [268]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: + + [269]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 [270]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 [271]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: + + [272]http://www.columbia.edu/kermit/ckermit70.html#x1.5 (explanation + of switches) + [273]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 + [274]http://www.columbia.edu/kermit/ckermit70.html#x1.6 and + [275]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 [276]Section 1.5.4 of the [277]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 ([278]Section 4) and character-set + translation ([279]Section 3.7). Synonym: /SUBDIRECTORIES. + + /UPDATE + Send only files that have changed since last time ([280]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 + [281]Section 3.7. + + /ERROR-ACTION:{PROCEED,QUIT} + Overrides the prevailing [282]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 [283]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 [284]FTP ERROR-ACTION selection. + + /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL} + Overrides the [285]FTP FILENAMES setting for this upload only. + + /PERMISSIONS:{ON,OFF} + Overrides the [286]FTP PERMISSIONS setting for this upload + only. + + /UNIQUE + Tells Kermit to tell the server to give [287]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 [288]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)([289]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 ([290]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 + ([291]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 + [292]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 ([293]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. + + [ [294]Top ] [ [295]FTP Top ] [ [296]C-Kermit Home ] [ [297]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 [298]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 + [299]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 [300]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 [301]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 [302]Section 3.6.3 for details. Synonym: + /RESTART. + + /FILENAMES:{CONVERTED,LITERAL} + Overrides the [303]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 [304]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 [305]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). [306]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 specifed 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 [307]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 [308]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 [309]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 [310]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 [311]Section 4.1 of the + [312]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 [313]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 [314]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 [315]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 [316]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). + + [ [317]Top ] [ [318]FTP Top ] [ [319]C-Kermit Home ] [ [320]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 capabilites 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 [321]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 [322]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, [323]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 [324]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. + + [ [325]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 [326]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). [327]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 ([328]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 [329]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 + + [330]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. + + [ [331]Top ] [ [332]FTP Top ] [ [333]C-Kermit Home ] [ [334]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". + + [ [335]Top ] [ [336]FTP Top ] [ [337]C-Kermit Home ] [ [338]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. + + [ [339]Top ] [ [340]FTP Top ] [ [341]C-Kermit Home ] [ [342]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. + + [343]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. See [344]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 [345]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 [346]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 "[347]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 veriable + 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...) + + [ [348]Top ] [ [349]FTP Top ] [ [350]FTP Script Tutorial ] [ + [351]C-Kermit Home ] [ [352]Kermit Home ] + _________________________________________________________________ + + 3.11. Advanced FTP Protocol Features + + The remainder of the FTP documention (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. + * [353]TERMINOLOGY + * [354]FEATURE NEGOTIATION + * [355]USING MGET: NLST VERSUS MLSD + * [356]EXAMPLES + * [357]REFERENCES + + The new releases of [358]C-Kermit (8.0.206) and [359]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 + [360]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 intepreted 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: [361]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: + [362]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: + [363]http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16 + .txt. + 4. [364]The Kermit FTP Client (overview). + + [ [365]Top ] [ [366]FTP Top ] [ [367]C-Kermit Home ] [ [368]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 [369]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 receivinging 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 + ([370]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 + appriately: + + 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. + + [ [371]Top ] [ [372]Contents ] [ [373]C-Kermit Home ] [ [374]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_ + + [ [375]Top ] [ [376]Contents ] [ [377]C-Kermit Home ] [ [378]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 [379]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. + + [ [380]Top ] [ [381]Contents ] [ [382]C-Kermit Home ] [ [383]Kermit + Home ] + __________________________________________________________________________ + +7. NEW COMMANDS AND SWITCHES + + See [384]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 [385]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 [386]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 + managment 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 ([387]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 { DOTIFILE, 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 connnection 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 listinga + DELETE RDEL LDEL Delete (a) file(s) + RENEME RREN LREN Rename a file + MKDIR RMKDIR LMKDIR Create a directory + RMDIR RRMDIR LRMDIR Remove a directory + + SET MATCH { DOTIFILE, 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 [388]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 [389]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. + + [ [390]Top ] [ [391]Contents ] [ [392]C-Kermit Home ] [ [393]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 ([394]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. + + [ [395]Top ] [ [396]Contents ] [ [397]C-Kermit Home ] [ [398]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 [399]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(...). + + [ [400]Top ] [ [401]Contents ] [ [402]C-Kermit Home ] [ [403]Kermit + Home ] + _________________________________________________________________ + + 8.3. New IF Conditions + + Several new IF conditions are available: + + IF DECLARED arrayname + Explained in [404]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. + + [ [405]Top ] [ [406]Contents ] [ [407]C-Kermit Home ] [ [408]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). + + [ [409]Top ] [ [410]Contents ] [ [411]C-Kermit Home ] [ [412]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. + + [ [413]Top ] [ [414]Contents ] [ [415]C-Kermit Home ] [ [416]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 [417]\fjoin() and [418]\fsplit(), plus + [419]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. + + [ [420]Top ] [ [421]Contents ] [ [422]C-Kermit Home ] [ [423]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 +built. + \v(ftime) Current time, secs since midnight, including fraction of se +cond. + \v(iprompt) The current SET PROMPT value + \v(sexp) The most recent S-Expression (see [424]Section 9) + \v(sdepth) The current S-Expression invocation depth ([425]Section 9) + \v(svalue) The value of the most recent S-Expression ([426]Section 9) + + \v(ftp_code) Most recent FTP response code ([427]Section 3) + \v(ftp_connected) FTP connection status ([428]Section 3) + \v(ftp_cpl) FTP Command Protection Level ([429]Section 3.2) + \v(ftp_dpl) FTP Data Protection Level ([430]Section 3.2) + \v(ftp_getputremote) The current SET GET-PUT-REMOTE setting ([431]Section 3.8 +) + \v(ftp_host) Name or IP address of FTP server ([432]Section 3) + \v(ftp_loggedin) FTP login status ([433]Section 3) + \v(ftp_message) Most recent FTP response message ([434]Section 3) + \v(ftp_security) FTP Security method ([435]Section 3.2) + \v(ftp_server) OS type of FTP server ([436]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() [437]Section 8.13, format options added + \fdelta2secs() [438]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 [439]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 combosed 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 + ([440]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 [441]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. + + [ [442]Top ] [ [443]Contents ] [ [444]C-Kermit Home ] [ [445]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. + + [ [446]Top ] [ [447]Contents ] [ [448]C-Kermit Home ] [ [449]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. + + [ [450]Top ] [ [451]Contents ] [ [452]C-Kermit Home ] [ [453]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 nineteeen 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 + [454]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 + + [ [455]Top ] [ [456]Contents ] [ [457]C-Kermit Home ] [ [458]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 [459]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). + + [ [460]Top ] [ [461]Contents ] [ [462]C-Kermit Home ] [ [463]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 + + [ [464]Top ] [ [465]Contents ] [ [466]C-Kermit Home ] [ [467]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 quantites 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 [468]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 [469]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 + [470]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 + [471]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 modifer 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 inteprets 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. + + [ [472]Top ] [ [473]Contents ] [ [474]C-Kermit Home ] [ [475]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 + [476]Section 8.1. + + [ [477]Top ] [ [478]Contents ] [ [479]C-Kermit Home ] [ [480]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 [481]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. + + [ [482]Top ] [ [483]Contents ] [ [484]C-Kermit Home ] [ [485]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 [486]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 [487]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 + ([488]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[] + + [ [489]Top ] [ [490]Contents ] [ [491]C-Kermit Home ] [ [492]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. + + [ [493]Top ] [ [494]Contents ] [ [495]C-Kermit Home ] [ [496]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 [497]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. + + [ [498]Top ] [ [499]Contents ] [ [500]C-Kermit Home ] [ [501]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 [502]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 [503]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 [504]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 operator, 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. + + [ [505]Top ] [ [506]Contents ] [ [507]C-Kermit Home ] [ [508]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 [509]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 + [510]Section 9.8. + + [ [511]Top ] [ [512]Contents ] [ [513]C-Kermit Home ] [ [514]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. + + [ [515]Top ] [ [516]Contents ] [ [517]C-Kermit Home ] [ [518]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... + + [ [519]Top ] [ [520]Contents ] [ [521]C-Kermit Home ] [ [522]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 [523]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 [524]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 apostophe (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. + + [ [525]Top ] [ [526]Contents ] [ [527]C-Kermit Home ] [ [528]Kermit + Home ] + _________________________________________________________________ + + 9.9. Examples + + 9.9.1. Statistics + + The following program computes statistics -- means, maxima, mimima, + 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. + + [ [529]Top ] [ [530]Contents ] [ [531]C-Kermit Home ] [ [532]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. + + [ [533]Top ] [ [534]Contents ] [ [535]C-Kermit Home ] [ [536]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 [537]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. + + [ [538]Top ] [ [539]Contents ] [ [540]C-Kermit Home ] [ [541]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 [542]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 + [543]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. + + [ [544]Top ] [ [545]Contents ] [ [546]C-Kermit Home ] [ [547]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 configuations 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. + + [ [548]Top ] [ [549]Contents ] [ [550]C-Kermit Home ] [ [551]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. + + [ [552]Top ] [ [553]Contents ] [ [554]C-Kermit Home ] [ [555]Kermit + Home ] + __________________________________________________________________________ + +13. CHARACTER SETS + + See the section on [556]file scanning above, and the section on + character-set conversion in [557]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 ([558]Section 3.7). + + [ [559]Top ] [ [560]Contents ] [ [561]C-Kermit Home ] [ [562]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, [563]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 Srdird link: + + [ [564]Top ] [ [565]Contents ] [ [566]Sredird ] [ [567]C-Kermit Home ] + [ [568]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 imbedded within devices. + This topic is covered [569]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 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.) + + [ [570]Top ] [ [571]Contents ] [ [572]C-Kermit Home ] [ [573]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 [574]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 [575]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. + + [ [576]Top ] [ [577]Contents ] [ [578]C-Kermit Home ] [ [579]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 + } + + [ [580]Top ] [ [581]Contents ] [ [582]C-Kermit Home ] [ [583]Kermit + Home ] + _________________________________________________________________ + + C-Kermit 8.0 Update Notes / [584]The Kermit Project / Columbia + University / 15 Dec 2003 + +References + + 1. http://www.columbia.edu/kermit/ckermit80.html#contents + 2. http://www.columbia.edu/kermit/ckermit.html + 3. http://www.columbia.edu/kermit/index.html + 4. http://www.columbia.edu/kermit/ckermit80.html + 5. mailto:kermit-support@columbia.edu + 6. http://www.columbia.edu/kermit/ + 7. http://www.kermit-project.org/ + 8. http://www.columbia.nyc.ny.us/kermit/ + 9. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT + 10. ftp://kermit.columbia.edu/kermit/f/ckcmai.c + 11. http://www.columbia.edu/kermit/ckermit80.html#xv + 12. http://www.columbia.edu/kermit/ck60manual.html + 13. http://www.columbia.edu/kermit/ckermi70.html + 14. ftp://kermit.columbia.edu/kermit/f/ckermit70.txt + 15. http://www.columbia.edu/kermit/ckututor.html + 16. ftp://kermit.columbia.edu/kermit/f/ckuker.nr + 17. http://www.columbia.edu/kermit/security.htm + 18. http://www.columbia.edu/kermit/telnet.htm + 19. http://www.columbia.edu/kermit/ftpscripts.html + 20. http://www.columbia.edu/kermit/ckcbwr.html + 21. ftp://kermit.columbia.edu/kermit/f/ckcbwr.txt + 22. http://www.columbia.edu/kermit/ckubwr.html + 23. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt + 24. http://www.columbia.edu/kermit/ckvbwr.html + 25. ftp://kermit.columbia.edu/kermit/f/ckvbwr.txt + 26. http://www.columbia.edu/kermit/ckuins.html + 27. ftp://kermit.columbia.edu/kermit/f/ckuins.txt + 28. http://www.columbia.edu/kermit/ckvins.html + 29. ftp://kermit.columbia.edu/kermit/f/ckvins.txt + 30. http://www.columbia.edu/kermit/ckccfg.html + 31. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt + 32. http://www.columbia.edu/kermit/ckcplm.html + 33. ftp://kermit.columbia.edu/kermit/f/ckcplm.txt + 34. http://www.columbia.edu/kermit/iksd.html + 35. http://www.columbia.edu/kermit/skermit.html + 36. http://www.columbia.edu/kermit/ckermit80.html#top + 37. http://www.columbia.edu/kermit/ckermit.html + 38. http://www.columbia.edu/kermit/index.html + 39. http://www.columbia.edu/kermit/ckermit80.html#x0 + 40. http://www.columbia.edu/kermit/ckermit80.html#x1 + 41. http://www.columbia.edu/kermit/ckermit80.html#x2 + 42. http://www.columbia.edu/kermit/ckermit80.html#x2.1 + 43. http://www.columbia.edu/kermit/ckermit80.html#x2.2 + 44. http://www.columbia.edu/kermit/ckermit80.html#x2.2.1 + 45. http://www.columbia.edu/kermit/ckermit80.html#x2.2.2 + 46. http://www.columbia.edu/kermit/ckermit80.html#x2.2.3 + 47. http://www.columbia.edu/kermit/ckermit80.html#x2.2.4 + 48. http://www.columbia.edu/kermit/ckermit80.html#x2.2.5 + 49. http://www.columbia.edu/kermit/ckermit80.html#x2.2.6 + 50. http://www.columbia.edu/kermit/ckermit80.html#x3 + 51. http://www.columbia.edu/kermit/ckermit80.html#x3.1 + 52. http://www.columbia.edu/kermit/ckermit80.html#x3.1.1 + 53. http://www.columbia.edu/kermit/ckermit80.html#x3.1.2 + 54. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 + 55. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 + 56. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 57. http://www.columbia.edu/kermit/ckermit80.html#x3.3 + 58. http://www.columbia.edu/kermit/ckermit80.html#x3.4 + 59. http://www.columbia.edu/kermit/ckermit80.html#x3.5 + 60. http://www.columbia.edu/kermit/ckermit80.html#x3.5.1 + 61. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 + 62. http://www.columbia.edu/kermit/ckermit80.html#x3.5.3 + 63. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 64. http://www.columbia.edu/kermit/ckermit80.html#x3.6.1 + 65. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 + 66. http://www.columbia.edu/kermit/ckermit80.html#x3.6.3 + 67. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 68. http://www.columbia.edu/kermit/ckermit80.html#x3.7.1 + 69. http://www.columbia.edu/kermit/ckermit80.html#x3.7.2 + 70. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 71. http://www.columbia.edu/kermit/ckermit80.html#x3.9 + 72. http://www.columbia.edu/kermit/ckermit80.html#x3.10 + 73. http://www.columbia.edu/kermit/ckermit80.html#x3.10.1 + 74. http://www.columbia.edu/kermit/ckermit80.html#x3.10.2 + 75. http://www.columbia.edu/kermit/ckermit80.html#x3.10.3 + 76. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 77. http://www.columbia.edu/kermit/ckermit80.html#x4 + 78. http://www.columbia.edu/kermit/ckermit80.html#x5 + 79. http://www.columbia.edu/kermit/ckermit80.html#x6 + 80. http://www.columbia.edu/kermit/ckermit80.html#x6.1 + 81. http://www.columbia.edu/kermit/ckermit80.html#x6.2 + 82. http://www.columbia.edu/kermit/ckermit80.html#x6.3 + 83. http://www.columbia.edu/kermit/ckermit80.html#x6.4 + 84. http://www.columbia.edu/kermit/ckermit80.html#x6.5 + 85. http://www.columbia.edu/kermit/ckermit80.html#x6.6 + 86. http://www.columbia.edu/kermit/ckermit80.html#x7 + 87. http://www.columbia.edu/kermit/ckermit80.html#x8 + 88. http://www.columbia.edu/kermit/ckermit80.html#x8.1 + 89. http://www.columbia.edu/kermit/ckermit80.html#x8.2 + 90. http://www.columbia.edu/kermit/ckermit80.html#x8.3 + 91. http://www.columbia.edu/kermit/ckermit80.html#x8.4 + 92. http://www.columbia.edu/kermit/ckermit80.html#x8.5 + 93. http://www.columbia.edu/kermit/ckermit80.html#x8.6 + 94. http://www.columbia.edu/kermit/ckermit80.html#x8.7 + 95. http://www.columbia.edu/kermit/ckermit80.html#x8.8 + 96. http://www.columbia.edu/kermit/ckermit80.html#x8.9 + 97. http://www.columbia.edu/kermit/ckermit80.html#x8.10 + 98. http://www.columbia.edu/kermit/ckermit80.html#x8.11 + 99. http://www.columbia.edu/kermit/ckermit80.html#x8.12 + 100. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 101. http://www.columbia.edu/kermit/ckermit80.html#x8.14 + 102. http://www.columbia.edu/kermit/ckermit80.html#x9 + 103. http://www.columbia.edu/kermit/ckermit80.html#x9.1 + 104. http://www.columbia.edu/kermit/ckermit80.html#x9.2 + 105. http://www.columbia.edu/kermit/ckermit80.html#x9.3 + 106. http://www.columbia.edu/kermit/ckermit80.html#x9.4 + 107. http://www.columbia.edu/kermit/ckermit80.html#x9.5 + 108. http://www.columbia.edu/kermit/ckermit80.html#x9.6 + 109. http://www.columbia.edu/kermit/ckermit80.html#x9.7 + 110. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 111. http://www.columbia.edu/kermit/ckermit80.html#x9.9 + 112. http://www.columbia.edu/kermit/ckermit80.html#x9.10 + 113. http://www.columbia.edu/kermit/ckermit80.html#x9.11 + 114. http://www.columbia.edu/kermit/ckermit80.html#x10 + 115. http://www.columbia.edu/kermit/ckermit80.html#x11 + 116. http://www.columbia.edu/kermit/ckermit80.html#x12 + 117. http://www.columbia.edu/kermit/ckermit80.html#x13 + 118. http://www.columbia.edu/kermit/ckermit80.html#x14 + 119. http://www.columbia.edu/kermit/ckermit80.html#x15 + 120. http://www.columbia.edu/kermit/ckermit80.html#x16 + 121. http://www.columbia.edu/kermit/ckermit80.html#x17 + 122. http://www.columbia.edu/kermit/ckermit80.html#top + 123. http://www.columbia.edu/kermit/ckermit.html + 124. http://www.columbia.edu/kermit/index.html + 125. http://www.columbia.edu/kermit/ckuins.html#x5 + 126. http://www.columbia.edu/kermit/ckuins.html + 127. http://www.columbia.edu/kermit/ckermit80.html#x5 + 128. http://www.columbia.edu/kermit/ckermit80.html#x2.2 + 129. http://www.columbia.edu/kermit/ckermit80.html#contents + 130. http://www.columbia.edu/kermit/ckermit80.html#x15 + 131. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 132. http://www.columbia.edu/kermit/ckermit80.html#ftpdates + 133. http://www.columbia.edu/kermit/ckermit80.html#ftpcheck + 134. http://www.columbia.edu/kermit/ckermit80.html#ftpnamelist + 135. http://www.columbia.edu/kermit/ckermit80.html#srvrename + 136. http://www.columbia.edu/kermit/ckermit80.html#ftpvdir + 137. http://www.columbia.edu/kermit/ckermit80.html#setftptype + 138. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 139. http://www.columbia.edu/kermit/ckermit80.html#x15 + 140. http://www.columbia.edu/kermit/ckermit80.html#x8.7 + 141. http://www.columbia.edu/kermit/ckermit80.html#x2.1 + 142. http://www.columbia.edu/kermit/ckermit80.html#x2.2 + 143. http://www.columbia.edu/kermit/ckermit80.html#x8.14 + 144. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 145. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 146. http://www.columbia.edu/kermit/ckututor.html + 147. http://www.columbia.edu/kermit/ckuins.html + 148. http://www.columbia.edu/kermit/skermit.html + 149. http://www.columbia.edu/kermit/ckermit80.html#setlocus + 150. http://www.columbia.edu/kermit/ckermit80.html#lcommands + 151. http://www.columbia.edu/kermit/ckermit80.html#ftpuser + 152. http://www.columbia.edu/kermit/ckermit80.html#showvar + 153. http://www.columbia.edu/kermit/ckermit80.html#callerid + 154. http://www.columbia.edu/kermit/ckermit80.html#x6.6 + 155. http://www.columbia.edu/kermit/ckermit80.html#x0 + 156. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 157. http://www.columbia.edu/kermit/ckermit80.html#top + 158. http://www.columbia.edu/kermit/ckermit80.html#contents + 159. http://www.columbia.edu/kermit/ckermit.html + 160. http://www.columbia.edu/kermit/index.html + 161. http://www.columbia.edu/kermit/ckermit80.html#x0 + 162. http://www.columbia.edu/kermit/ckermit80.html#top + 163. http://www.columbia.edu/kermit/ckermit80.html#contents + 164. http://www.columbia.edu/kermit/ckermit.html + 165. http://www.columbia.edu/kermit/index.html + 166. http://www.columbia.edu/kermit/k95.html + 167. http://www.columbia.edu/kermit/sshclient.html + 168. http://www.columbia.edu/kermit/skermit.html + 169. http://www.columbia.edu/kermit/skermit.html + 170. http://www.columbia.edu/kermit/sshclien.htm + 171. http://www.columbia.edu/kermit/ckermit80.html#x3 + 172. ftp://ftp.isi.edu/in-notes/rfc1738.txt + 173. http://www.columbia.edu/kermit/ckermit80.html#x2.2.2 + 174. http://www.columbia.edu/kermit/ckermit80.html#x2.2.1 + 175. ftp://ftp.isi.edu/in-notes/rfc2396.txt + 176. ftp://ftp.isi.edu/in-notes/rfc2616.txt + 177. http://www.columbia.edu/kermit/ckermit80.html#x2.2.3 + 178. ftp://ftp.isi.edu/in-notes/rfc2616.txt + 179. http://www.columbia.edu/kermit/ckermit80.html#x8.13.7 + 180. http://www.columbia.edu/kermit/security.htm#x5.4 + 181. http://www.columbia.edu/kermit/security.htm#x15 + 182. http://www.columbia.edu/kermit/security.htm#x6.2 + 183. http://www.columbia.edu/kermit/security.html + 184. http://www.columbia.edu/kermit/ckermit80.html#x16 + 185. http://www.columbia.edu/kermit/ckermit80.html#top + 186. http://www.columbia.edu/kermit/ckermit80.html#contents + 187. http://www.columbia.edu/kermit/ckermit.html + 188. http://www.columbia.edu/kermit/index.html + 189. http://www.columbia.edu/kermit/ckermit80.html#x3.1 + 190. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 191. http://www.columbia.edu/kermit/ckermit80.html#x3.3 + 192. http://www.columbia.edu/kermit/ckermit80.html#x3.4 + 193. http://www.columbia.edu/kermit/ckermit80.html#x3.5 + 194. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 195. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 196. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 197. http://www.columbia.edu/kermit/ckermit80.html#x3.9 + 198. http://www.columbia.edu/kermit/ckermit80.html#x3.10 + 199. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 200. http://www.columbia.edu/kermit/security.htm + 201. http://www.columbia.edu/kermit/security.htm#servers + 202. http://www.columbia.edu/kermit/ckcsets.html + 203. http://www.columbia.edu/kermit/unicode.html + 204. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 + 205. http://www.columbia.edu/kermit/case10.html + 206. http://www.columbia.edu/kermit/ckermit80.html#x4 + 207. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 208. http://www.columbia.edu/kermit/ftpscripts.html + 209. http://www.columbia.edu/kermit/ckermit80.html#top + 210. http://www.columbia.edu/kermit/ckermit80.html#ftp + 211. http://www.columbia.edu/kermit/ftpclient.html + 212. http://www.columbia.edu/kermit/ftpscripts.html + 213. http://www.columbia.edu/kermit/ckermit.html + 214. http://www.columbia.edu/kermit/index.html + 215. http://www.columbia.edu/kermit/ckermit80.html#x3.1.1 + 216. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 + 217. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 + 218. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 + 219. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 + 220. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 221. http://www.columbia.edu/kermit/ckermit80.html#x3.5 + 222. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 223. http://www.columbia.edu/kermit/ftpscripts.html + 224. http://www.columbia.edu/kermit/ckb2.htm + 225. http://www.columbia.edu/kermit/ckermit80.html#ftpautolog + 226. http://www.columbia.edu/kermit/ckermit80.html#ftpuser + 227. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 228. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 229. http://www.columbia.edu/kermit/ckermit80.html#top + 230. http://www.columbia.edu/kermit/ckermit80.html#ftp + 231. http://www.columbia.edu/kermit/ckermit.html + 232. http://www.columbia.edu/kermit/index.html + 233. http://www.columbia.edu/kermit/ibm_ie.html + 234. http://www.columbia.edu/kermit/ckermit80.html#x3.10 + 235. http://www.columbia.edu/kermit/ckermit80.html#top + 236. http://www.columbia.edu/kermit/ckermit80.html#ftp + 237. http://www.columbia.edu/kermit/ckermit.html + 238. http://www.columbia.edu/kermit/index.html + 239. http://www.columbia.edu/kermit/ck60manual.html + 240. http://www.columbia.edu/kermit/ckermit70.html#x4.17 + 241. http://www.columbia.edu/kermit/ckermit70.html + 242. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 243. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 244. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 + 245. http://www.columbia.edu/kermit/security.html + 246. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 247. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 248. http://www.columbia.edu/kermit/ckermit80.html#x8.13.4 + 249. http://www.columbia.edu/kermit/ckermit80.html#permswitch + 250. http://www.columbia.edu/kermit/ckermit80.html#ftpchmod + 251. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 + 252. http://www.columbia.edu/kermit/ckermit80.html#x4 + 253. http://www.columbia.edu/kermit/ckermit80.html#top + 254. http://www.columbia.edu/kermit/ckermit80.html#ftp + 255. http://www.columbia.edu/kermit/ckermit.html + 256. http://www.columbia.edu/kermit/index.html + 257. http://www.columbia.edu/kermit/ckermit80.html#x7 + 258. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 259. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 260. http://www.columbia.edu/kermit/ckb2.htm + 261. http://www.columbia.edu/kermit/ckermit80.html#x3.10 + 262. http://www.columbia.edu/kermit/ckermit80.html#x3.10 + 263. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 264. http://www.columbia.edu/kermit/ckermit80.html#setftptype + 265. http://www.columbia.edu/kermit/ckermit80.html#top + 266. http://www.columbia.edu/kermit/ckermit80.html#ftp + 267. http://www.columbia.edu/kermit/ckermit.html + 268. http://www.columbia.edu/kermit/index.html + 269. http://www.columbia.edu/kermit/ckermit70.html#x4.9 + 270. http://www.columbia.edu/kermit/ckermit80.html#x3.5.1 + 271. http://www.columbia.edu/kermit/ckermit80.html#erroraction + 272. http://www.columbia.edu/kermit/ckermit70.html#x1.5 + 273. http://www.columbia.edu/kermit/ckermit70.html#x4.7 + 274. http://www.columbia.edu/kermit/ckermit70.html#x1.6 + 275. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 276. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 + 277. http://www.columbia.edu/kermit/ckermi70.htm + 278. http://www.columbia.edu/kermit/ckermit80.html#x4 + 279. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 280. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 + 281. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 282. http://www.columbia.edu/kermit/ckermit80.html#erroraction + 283. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 + 284. http://www.columbia.edu/kermit/ckermit80.html#erroraction + 285. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames + 286. http://www.columbia.edu/kermit/ckermit80.html#ftpperms + 287. http://www.columbia.edu/kermit/ckermit80.html#ftpunique + 288. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames + 289. http://www.columbia.edu/kermit/ckermit80.html#note_utc + 290. http://www.columbia.edu/kermit/ckermit80.html#note_date + 291. http://www.columbia.edu/kermit/ckermit80.html#x3.6 + 292. http://www.boulder.nist.gov/timefreq/faq/faq.htm#10: + 293. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 294. http://www.columbia.edu/kermit/ckermit80.html#top + 295. http://www.columbia.edu/kermit/ckermit80.html#ftp + 296. http://www.columbia.edu/kermit/ckermit.html + 297. http://www.columbia.edu/kermit/index.html + 298. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 299. http://www.columbia.edu/kermit/ckermi70.htm#x4.3 + 300. http://www.columbia.edu/kermit/ckermit70.html + 301. http://www.columbia.edu/kermit/ckermit80.html#x5 + 302. http://www.columbia.edu/kermit/ckermit80.html#x3.6.3 + 303. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames + 304. http://www.columbia.edu/kermit/ckermi70.htm#x4.1 + 305. http://www.columbia.edu/kermit/ckermi70.htm#x4.2.2 + 306. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 + 307. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 + 308. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 309. http://www.columbia.edu/kermit/ckermit80.html#x3.11 + 310. http://www.columbia.edu/kermit/ckermit80.html#srvrename + 311. http://www.columbia.edu/kermit/ckermi70.htm#x4.1 + 312. http://www.columbia.edu/kermit/ckermi70.htm + 313. http://www.columbia.edu/kermit/ckb2.htm + 314. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames + 315. http://www.columbia.edu/kermit/ckermit80.html#x3.5.3 + 316. http://www.proftpd.net/ + 317. http://www.columbia.edu/kermit/ckermit80.html#top + 318. http://www.columbia.edu/kermit/ckermit80.html#ftp + 319. http://www.columbia.edu/kermit/ckermit.html + 320. http://www.columbia.edu/kermit/index.html + 321. http://www.columbia.edu/kermit/ckb2.htm + 322. http://www.columbia.edu/kermit/ckcsets.html + 323. http://www.columbia.edu/kermit/unicode.html + 324. http://www.columbia.edu/kermit/ckcsets.html + 325. http://www.columbia.edu/kermit/ckcsets.html + 326. http://www.columbia.edu/kermit/ckermit80.html#x4 + 327. http://www.columbia.edu/kermit/utf8.html + 328. http://www.columbia.edu/kermit/ckcsets.html + 329. http://www.columbia.edu/kermit/ckermit80.html#x4 + 330. ftp://ftp.isi.edu/in-notes/rfc2640.txt + 331. http://www.columbia.edu/kermit/ckermit80.html#top + 332. http://www.columbia.edu/kermit/ckermit80.html#ftp + 333. http://www.columbia.edu/kermit/ckermit.html + 334. http://www.columbia.edu/kermit/index.html + 335. http://www.columbia.edu/kermit/ckermit80.html#top + 336. http://www.columbia.edu/kermit/ckermit80.html#ftp + 337. http://www.columbia.edu/kermit/ckermit.html + 338. http://www.columbia.edu/kermit/index.html + 339. http://www.columbia.edu/kermit/ckermit80.html#top + 340. http://www.columbia.edu/kermit/ckermit80.html#ftp + 341. http://www.columbia.edu/kermit/ckermit.html + 342. http://www.columbia.edu/kermit/index.html + 343. http://www.columbia.edu/kermit/ftpscripts.html + 344. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 345. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 346. ftp://ftp.isi.edu/in-notes/rfc959.txt + 347. http://www.columbia.edu/kermit/ckscripts.html + 348. http://www.columbia.edu/kermit/ckermit80.html#top + 349. http://www.columbia.edu/kermit/ckermit80.html#ftp + 350. http://www.columbia.edu/kermit/ftpscript.html + 351. http://www.columbia.edu/kermit/ckermit.html + 352. http://www.columbia.edu/kermit/index.html + 353. http://www.columbia.edu/kermit/ckermit80.html#x3.11.1 + 354. http://www.columbia.edu/kermit/ckermit80.html#x3.11.2 + 355. http://www.columbia.edu/kermit/ckermit80.html#x3.11.3 + 356. http://www.columbia.edu/kermit/ckermit80.html#x3.11.4 + 357. http://www.columbia.edu/kermit/ckermit80.html#x3.11.5 + 358. http://www.columbia.edu/kermit/ckermit.html + 359. http://www.columbia.edu/kermit/k95.html + 360. http://www.columbia.edu/kermit/ckermit80.html#x3.11.5 + 361. ftp://ftp.isi.edu/in-notes/rfc959.txt + 362. ftp://ftp.isi.edu/in-notes/rfc2389.txt + 363. http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt + 364. http://www.columbia.edu/kermit/ftpclient.html + 365. http://www.columbia.edu/kermit/ckermit80.html#top + 366. http://www.columbia.edu/kermit/ckermit80.html#ftp + 367. http://www.columbia.edu/kermit/ckermit.html + 368. http://www.columbia.edu/kermit/index.html + 369. http://www.columbia.edu/kermit/ckermit80.html#x3 + 370. http://www.columbia.edu/kermit/ckermit80.html#ucs2 + 371. http://www.columbia.edu/kermit/ckermit80.html#top + 372. http://www.columbia.edu/kermit/ckermit80.html#contents + 373. http://www.columbia.edu/kermit/ckermit.html + 374. http://www.columbia.edu/kermit/index.html + 375. http://www.columbia.edu/kermit/ckermit80.html#top + 376. http://www.columbia.edu/kermit/ckermit80.html#contents + 377. http://www.columbia.edu/kermit/ckermit.html + 378. http://www.columbia.edu/kermit/index.html + 379. http://www.columbia.edu/kermit/ckb2.htm + 380. http://www.columbia.edu/kermit/ckermit80.html#top + 381. http://www.columbia.edu/kermit/ckermit80.html#contents + 382. http://www.columbia.edu/kermit/ckermit.html + 383. http://www.columbia.edu/kermit/index.html + 384. http://www.columbia.edu/kermit/ckermit80.html#x4 + 385. http://www.columbia.edu/kermit/ckermit80.html#x4 + 386. http://www.columbia.edu/kermit/ckermit80.html#x8.12 + 387. http://www.columbia.edu/kermit/ckermit80.html#x8.1 + 388. http://www.columbia.edu/kermit/ckermit80.html#x12 + 389. http://www.columbia.edu/kermit/ckermit80.html#x8.12 + 390. http://www.columbia.edu/kermit/ckermit80.html#top + 391. http://www.columbia.edu/kermit/ckermit80.html#contents + 392. http://www.columbia.edu/kermit/ckermit.html + 393. http://www.columbia.edu/kermit/index.html + 394. http://www.columbia.edu/kermit/ckermit80.html#x8.14 + 395. http://www.columbia.edu/kermit/ckermit80.html#top + 396. http://www.columbia.edu/kermit/ckermit80.html#contents + 397. http://www.columbia.edu/kermit/ckermit.html + 398. http://www.columbia.edu/kermit/index.html + 399. http://www.columbia.edu/kermit/ckermit80.html#x9 + 400. http://www.columbia.edu/kermit/ckermit80.html#top + 401. http://www.columbia.edu/kermit/ckermit80.html#contents + 402. http://www.columbia.edu/kermit/ckermit.html + 403. http://www.columbia.edu/kermit/index.html + 404. http://www.columbia.edu/kermit/ckermit80.html#x8.6 + 405. http://www.columbia.edu/kermit/ckermit80.html#top + 406. http://www.columbia.edu/kermit/ckermit80.html#contents + 407. http://www.columbia.edu/kermit/ckermit.html + 408. http://www.columbia.edu/kermit/index.html + 409. http://www.columbia.edu/kermit/ckermit80.html#top + 410. http://www.columbia.edu/kermit/ckermit80.html#contents + 411. http://www.columbia.edu/kermit/ckermit.html + 412. http://www.columbia.edu/kermit/index.html + 413. http://www.columbia.edu/kermit/ckermit80.html#top + 414. http://www.columbia.edu/kermit/ckermit80.html#contents + 415. http://www.columbia.edu/kermit/ckermit.html + 416. http://www.columbia.edu/kermit/index.html + 417. http://www.columbia.edu/kermit/ckermit80.html#fjoin + 418. http://www.columbia.edu/kermit/ckermit80.html#fsplit + 419. http://www.columbia.edu/kermit/ckermit80.html#x8.10 + 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#x9 + 425. http://www.columbia.edu/kermit/ckermit80.html#x9 + 426. http://www.columbia.edu/kermit/ckermit80.html#x9 + 427. http://www.columbia.edu/kermit/ckermit80.html#x3 + 428. http://www.columbia.edu/kermit/ckermit80.html#x3 + 429. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 430. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 431. http://www.columbia.edu/kermit/ckermit80.html#x3.8 + 432. http://www.columbia.edu/kermit/ckermit80.html#x3 + 433. http://www.columbia.edu/kermit/ckermit80.html#x3 + 434. http://www.columbia.edu/kermit/ckermit80.html#x3 + 435. http://www.columbia.edu/kermit/ckermit80.html#x3.2 + 436. http://www.columbia.edu/kermit/ckermit80.html#x3 + 437. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 438. http://www.columbia.edu/kermit/ckermit80.html#x8.13 + 439. http://www.columbia.edu/kermit/ckermit80.html#x9 + 440. http://www.columbia.edu/kermit/ckermit80.html#x8.10 + 441. http://www.columbia.edu/kermit/ckermit80.html#x8.7.4 + 442. http://www.columbia.edu/kermit/ckermit80.html#top + 443. http://www.columbia.edu/kermit/ckermit80.html#contents + 444. http://www.columbia.edu/kermit/ckermit.html + 445. http://www.columbia.edu/kermit/index.html + 446. http://www.columbia.edu/kermit/ckermit80.html#top + 447. http://www.columbia.edu/kermit/ckermit80.html#contents + 448. http://www.columbia.edu/kermit/ckermit.html + 449. http://www.columbia.edu/kermit/index.html + 450. http://www.columbia.edu/kermit/ckermit80.html#top + 451. http://www.columbia.edu/kermit/ckermit80.html#contents + 452. http://www.columbia.edu/kermit/ckermit.html + 453. http://www.columbia.edu/kermit/index.html + 454. http://www.columbia.edu/kermit/ckermit80.html#x8.7 + 455. http://www.columbia.edu/kermit/ckermit80.html#top + 456. http://www.columbia.edu/kermit/ckermit80.html#contents + 457. http://www.columbia.edu/kermit/ckermit.html + 458. http://www.columbia.edu/kermit/index.html + 459. http://www.columbia.edu/kermit/ckermit80.html#scriptedit + 460. http://www.columbia.edu/kermit/ckermit80.html#top + 461. http://www.columbia.edu/kermit/ckermit80.html#contents + 462. http://www.columbia.edu/kermit/ckermit.html + 463. http://www.columbia.edu/kermit/index.html + 464. http://www.columbia.edu/kermit/ckermit80.html#top + 465. http://www.columbia.edu/kermit/ckermit80.html#contents + 466. http://www.columbia.edu/kermit/ckermit.html + 467. http://www.columbia.edu/kermit/index.html + 468. ftp://ftp.isi.edu/in-notes/rfc2822.txt + 469. ftp://ftp.isi.edu/in-notes/rfc2822.txt + 470. ftp://ftp.isi.edu/in-notes/rfc2822.txt + 471. ftp://ftp.isi.edu/in-notes/rfc2822.txt + 472. http://www.columbia.edu/kermit/ckermit80.html#top + 473. http://www.columbia.edu/kermit/ckermit80.html#contents + 474. http://www.columbia.edu/kermit/ckermit.html + 475. http://www.columbia.edu/kermit/index.html + 476. http://www.columbia.edu/kermit/ckermit80.html#x8.1 + 477. http://www.columbia.edu/kermit/ckermit80.html#top + 478. http://www.columbia.edu/kermit/ckermit80.html#contents + 479. http://www.columbia.edu/kermit/ckermit.html + 480. http://www.columbia.edu/kermit/index.html + 481. http://www.columbia.edu/kermit/ckermit80.html#x8.2 + 482. http://www.columbia.edu/kermit/ckermit80.html#top + 483. http://www.columbia.edu/kermit/ckermit80.html#contents + 484. http://www.columbia.edu/kermit/ckermit.html + 485. http://www.columbia.edu/kermit/index.html + 486. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 487. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 488. http://www.columbia.edu/kermit/ckermit80.html#x8.2 + 489. http://www.columbia.edu/kermit/ckermit80.html#top + 490. http://www.columbia.edu/kermit/ckermit80.html#contents + 491. http://www.columbia.edu/kermit/ckermit.html + 492. http://www.columbia.edu/kermit/index.html + 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#top + 499. http://www.columbia.edu/kermit/ckermit80.html#contents + 500. http://www.columbia.edu/kermit/ckermit.html + 501. http://www.columbia.edu/kermit/index.html + 502. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 503. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 504. http://www.columbia.edu/kermit/ckermit80.html#x9.6 + 505. http://www.columbia.edu/kermit/ckermit80.html#top + 506. http://www.columbia.edu/kermit/ckermit80.html#contents + 507. http://www.columbia.edu/kermit/ckermit.html + 508. http://www.columbia.edu/kermit/index.html + 509. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 510. http://www.columbia.edu/kermit/ckermit80.html#x9.8 + 511. http://www.columbia.edu/kermit/ckermit80.html#top + 512. http://www.columbia.edu/kermit/ckermit80.html#contents + 513. http://www.columbia.edu/kermit/ckermit.html + 514. http://www.columbia.edu/kermit/index.html + 515. http://www.columbia.edu/kermit/ckermit80.html#top + 516. http://www.columbia.edu/kermit/ckermit80.html#contents + 517. http://www.columbia.edu/kermit/ckermit.html + 518. http://www.columbia.edu/kermit/index.html + 519. http://www.columbia.edu/kermit/ckermit80.html#top + 520. http://www.columbia.edu/kermit/ckermit80.html#contents + 521. http://www.columbia.edu/kermit/ckermit.html + 522. http://www.columbia.edu/kermit/index.html + 523. mailto:thucdat@hotmail.com + 524. http://www.columbia.edu/kermit/ckermit80.html#x9.9.2 + 525. http://www.columbia.edu/kermit/ckermit80.html#top + 526. http://www.columbia.edu/kermit/ckermit80.html#contents + 527. http://www.columbia.edu/kermit/ckermit.html + 528. http://www.columbia.edu/kermit/index.html + 529. http://www.columbia.edu/kermit/ckermit80.html#top + 530. http://www.columbia.edu/kermit/ckermit80.html#contents + 531. http://www.columbia.edu/kermit/ckermit.html + 532. http://www.columbia.edu/kermit/index.html + 533. http://www.columbia.edu/kermit/ckermit80.html#top + 534. http://www.columbia.edu/kermit/ckermit80.html#contents + 535. http://www.columbia.edu/kermit/ckermit.html + 536. http://www.columbia.edu/kermit/index.html + 537. http://www.columbia.edu/kermit/ckermit80.html#x9.2 + 538. http://www.columbia.edu/kermit/ckermit80.html#top + 539. http://www.columbia.edu/kermit/ckermit80.html#contents + 540. http://www.columbia.edu/kermit/ckermit.html + 541. http://www.columbia.edu/kermit/index.html + 542. http://www.columbia.edu/kermit/ckermit80.html#x4 + 543. http://www.columbia.edu/kermit/ckermit80.html#x4 + 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#top + 549. http://www.columbia.edu/kermit/ckermit80.html#contents + 550. http://www.columbia.edu/kermit/ckermit.html + 551. http://www.columbia.edu/kermit/index.html + 552. http://www.columbia.edu/kermit/ckermit80.html#top + 553. http://www.columbia.edu/kermit/ckermit80.html#contents + 554. http://www.columbia.edu/kermit/ckermit.html + 555. http://www.columbia.edu/kermit/index.html + 556. http://www.columbia.edu/kermit/ckermit80.html#x4 + 557. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 558. http://www.columbia.edu/kermit/ckermit80.html#x3.7 + 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. ftp://ftp.isi.edu/in-notes/rfc2217.txt + 564. http://www.columbia.edu/kermit/ckermit80.html#top + 565. http://www.columbia.edu/kermit/ckermit80.html#contents + 566. ftp://kermit.columbia.edu/kermit/sredird/ + 567. http://www.columbia.edu/kermit/ckermit.html + 568. http://www.columbia.edu/kermit/index.html + 569. http://www.columbia.edu/kermit/ckermi70.htm#x4.22 + 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. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 + 575. http://www.columbia.edu/kermit/cuiksd.html + 576. http://www.columbia.edu/kermit/ckermit80.html#top + 577. http://www.columbia.edu/kermit/ckermit80.html#contents + 578. http://www.columbia.edu/kermit/ckermit.html + 579. http://www.columbia.edu/kermit/index.html + 580. http://www.columbia.edu/kermit/ckermit80.html#top + 581. http://www.columbia.edu/kermit/ckermit80.html#contents + 582. http://www.columbia.edu/kermit/ckermit.html + 583. http://www.columbia.edu/kermit/index.html + 584. http://www.columbia.edu/kermit/index.html diff --git a/ckermod.ini b/ckermod.ini new file mode 100644 index 0000000..5848c3f --- /dev/null +++ b/ckermod.ini @@ -0,0 +1,144 @@ +; 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) diff --git a/ckuat2.h b/ckuat2.h new file mode 100644 index 0000000..5ee8e10 --- /dev/null +++ b/ckuat2.h @@ -0,0 +1,363 @@ +/* + C K U A T 2 . H -- Kerberos headers for C-Kermit + + Copyright (C) 1985, 2004, + 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 + +#ifndef CRYPT_DLL +extern int (*decrypt_input) P((int)); +extern void (*encrypt_output) P((unsigned char *, int)); +#endif /* CRYPT_DLL */ + +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 *,long); + 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 */ diff --git a/ckuath.c b/ckuath.c new file mode 100644 index 0000000..0ba4360 --- /dev/null +++ b/ckuath.c @@ -0,0 +1,13287 @@ +char *ckathv = "Authentication, 8.0.232, 7 Feb 2004"; +/* + C K U A T H . C -- Authentication for C-Kermit + + Copyright (C) 1999, 2004, + 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 +#include +#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 +#define OPENSSL_ENABLE_OLD_DES_SUPPORT +#include +#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" +#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 + hexdump("from plaintext", s, n); +#endif + i.ptr = s; + i.length = n; + g_kstream->encrypt(&i, NULL); +#ifdef DEBUG + hexdump("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 + hexdump("from cyphertext", s, n); +#endif + + i.ptr = s; + i.length = n; + g_kstream->decrypt(&i, NULL); +#ifdef DEBUG + hexdump("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; + } + hexdump("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 */ + hexdump("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 */ + hexdump( + "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; + } + + hexdump("KRB4_RESPONSE &parsedat[5]",&parsedat[5],8); +#ifdef CK_ENCRYPTION + hexdump("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; + hexdump("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; + } + hexdump("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 */ + hexdump("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,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 = 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; + + hexdump("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); + hexdump("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); + hexdump("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; + + hexdump("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 */ + { + hexdump("SRP_RESPONSE ts",ts,sizeof(ts)); + hexdump("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(hostname); + if (!realm) { + ckstrncpy(strTmp, "Can't find realm for host \"",AUTHTMPBL); + ckstrncat(strTmp, 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(term_speed)+strlen(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)),":", + term_speed,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, 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(remoteuser)+1); + (void) ttol(term_speed, strlen(term_speed)+1); + (void) ttol(localuser, strlen(localuser)+1); + + if (forward_flag) { /* Forward credentials (global) */ + if (status = krb5_fwd_tgt_creds( k5_context, + auth_context, + 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(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, + &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); + + hexdump("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 */ + hexdump("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 */ + hexdump("krb4_des_write garbage_buf",garbage_buf,8); + } else + hexdump("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 ) + hexdump("krb4_des_write (post pcbc) garbage_buf",garbage_buf,8); + else + hexdump("krb4_des_write (post pcbc) buf",buf,8); + hexdump("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); + hexdump("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 + if (k5_context) + krb5_init_ets(k5_context); +#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; + } +} +#endif /* CK_SECURITY */ diff --git a/ckuath.h b/ckuath.h new file mode 100644 index 0000000..0dbe8dc --- /dev/null +++ b/ckuath.h @@ -0,0 +1,228 @@ +/* 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, 2004, + 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. */ + +#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*/ diff --git a/ckubwr.txt b/ckubwr.txt new file mode 100644 index 0000000..4b0d387 --- /dev/null +++ b/ckubwr.txt @@ -0,0 +1,5330 @@ + + C-Kermit 8.0 Unix Hints and Tips + + Frank da Cruz + [1]The Kermit Project, [2]Columbia University + + As of: C-Kermit 8.0.211 10 April 2004 + This page last updated: Fri Apr 16 16:13:14 2004 (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: + + [3]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. [4]Feedback from experts on + particular OS's and platforms is always welcome. + + [ [5]C-Kermit ] [ [6]Installation Instructions ] [ [7]TUTORIAL ] + ________________________________________________________________________ + + CONTENTS + + 1. [8]INTRODUCTION + 2. [9]PREBUILT C-KERMIT BINARIES + 3. [10]PLATFORM-SPECIFIC NOTES + 4. [11]GENERAL UNIX-SPECIFIC LIMITATIONS AND BUGS + 5. [12]INITIALIZATION AND COMMAND FILES + 6. [13]COMMUNICATION SPEED SELECTION + 7. [14]COMMUNICATIONS AND DIALING + 8. [15]HARDWARE FLOW CONTROL + 9. [16]TERMINAL CONNECTION AND KEY MAPPING + 10. [17]FILE TRANSFER + 11. [18]EXTERNAL FILE TRANSFER PROTOCOLS + 12. [19]SECURITY + 13. [20]MISCELLANEOUS USER REPORTS + 14. [21]THIRD-PARTY DRIVERS + + Quick Links: [ [22]Linux ] [ [23]*BSD ] [[24]Mac OS X] [ [25]AIX ] [ + [26]HP-UX ] [ [27]Solaris ] [ [28]SCO ] [ [29]DEC/Compaq ] + ________________________________________________________________________ + + 1. INTRODUCTION + + [ [30]Top ] [ [31]Contents ] [ [32]Next ] + + SECTION CONTENTS + + 1.1. [33]Documentation + 1.2. [34]Technical Support + 1.3. [35]The Year 2000 + 1.4. [36]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 [37]Linux, [38]AIX, [39]HP-UX, + [40]Solaris, and so on, and should be read in conjunction with the + [41]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 [42]let + us know. + + 1.1. Documentation + + [ [43]Top ] [ [44]Contents ] [ [45]Next ] + + C-Kermit 6.0 is documented in the book [46]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: + + [47]Supplement to Using C-Kermit, Second Edition, For C-Kermit 7.0 + Thorough documentation of features new to version 7.0. + + [48]Supplement to Using C-Kermit, Second Edition, For C-Kermit 8.0 + Thorough documentation of features new to version 8.0. + + 1.2. Technical Support + + [ [49]Top ] [ [50]Contents ] [ [51]Section Contents ] [ [52]Next ] [ + [53]Previous ] + + For information on how to get technical support, please visit: + + [54]http://www.columbia.edu/kermit/support.html + + 1.3. The Year 2000 + + [ [55]Top ] [ [56]Contents ] [ [57]Section Contents ] [ [58]Next ] [ + [59]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 + + [ [60]Top ] [ [61]Contents ] [ [62]Section Contents ] [ [63]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 + + [ [64]Top ] [ [65]Contents ] [ [66]Next ] [ [67]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 + + [ [68]Top ] [ [69]Contents ] [ [70]Next ] [ [71]Previous ] + + SECTION CONTENTS + + 3.0. [72]C-KERMIT ON PC-BASED UNIXES + 3.1. [73]C-KERMIT AND AIX + 3.2. [74]C-KERMIT AND HP-UX + 3.3. [75]C-KERMIT AND LINUX + 3.4. [76]C-KERMIT AND NEXTSTEP + 3.5. [77]C-KERMIT AND QNX + 3.6. [78]C-KERMIT AND SCO + 3.7. [79]C-KERMIT AND SOLARIS + 3.8. [80]C-KERMIT AND SUNOS + 3.9. [81]C-KERMIT AND ULTRIX + 3.10. [82]C-KERMIT AND UNIXWARE + 3.11. [83]C-KERMIT AND APOLLO SR10 + 3.12. [84]C-KERMIT AND TANDY XENIX 3.0 + 3.13. [85]C-KERMIT AND OSF/1 (DIGITAL UNIX) (TRU64 UNIX) + 3.14. [86]C-KERMIT AND SGI IRIX + 3.15. [87]C-KERMIT AND THE BEBOX + 3.16. [88]C-KERMIT AND DG/UX + 3.17. [89]C-KERMIT AND SEQUENT DYNIX + 3.18. [90]C-KERMIT AND {FREE,OPEN,NET}BSD + 3.19. [91]C-KERMIT AND MAC OS X + 3.20. [92]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: + + [93]http://www.faqs.org + [94]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 + [95]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 [96]send us your suggested fixes or changes. Or + else just [97]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 + + [ [98]Top ] [ [99]Contents ] [ [100]Section Contents ] [ [101]Next ] + + Also see: [102]http://www.pcunix.com/. + + SECTION CONTENTS + + 3.0.1. [103]Interrupt Conflicts + 3.0.2. [104]Windows-Specific Hardware + 3.0.3. [105]Modems + 3.0.4. [106]Character Sets + 3.0.5. [107]Keyboard, Screen, and Mouse Access + 3.0.6. [108]Laptops + + 3.0.1. Interrupt Conflicts + + [ [109]Top ] [ [110]Contents ] [ [111]Section Contents ] [ [112]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 + + [ [113]Top ] [ [114]Contents ] [ [115]Section Contents ] [ [116]Next ] + [ [117]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 + [118]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 [119]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 + + [ [120]Top ] [ [121]Contents ] [ [122]Section Contents ] [ [123]Next ] + [ [124]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: + + [125]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 [126]DigiBoard are available for certain Unix platforms + (typically SCO, maybe Linux) that come with special platform-specific + drivers. + ________________________________________________________________________ + + 3.0.4. Character Sets + + [ [127]Top ] [ [128]Contents ] [ [129]Section Contents ] [ [130]Next ] + [ [131]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 + + [ [132]Top ] [ [133]Contents ] [ [134]Section Contents ] [ [135]Next ] + [ [136]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 + + [ [137]Top ] [ [138]Contents ] [ [139]Section Contents ] [ + [140]Previous ] + + (To be filled in . . .) + ________________________________________________________________________ + + 3.1. C-KERMIT AND AIX + + [ [141]Top ] [ [142]Contents ] [ [143]Section Contents ] [ [144]Next ] + [ [145]Previous ] + + SECTION CONTENTS + + 3.1.1. [146]AIX: General + 3.1.2. [147]AIX: Network Connections + 3.1.3. [148]AIX: Serial Connections + 3.1.4. [149]AIX: File Transfer + 3.1.5. [150]AIX: Xterm Key Map + + For additional information see: + * [151]http://www.emerson.emory.edu/services/aix-faq/ + * [152]http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.aix.html + * [153]http://www.cis.ohio-state.edu/hypertext/faq/usenet/aix-faq/to + p.html + * [154]http://aixpdslib.seas.ucla.edu/ + * [155]http://www.rootvg.net (AIX history) + * [156]ftp://rtfm.mit.edu/pub/usenet/news.answers/aix-faq/part1 + * [157]ftp://mirrors.aol.com/pub/rtfm/usenet-by-hierarchy/comp/unix/ + aix + + and/or read the [158]comp.unix.aix newsgroup. + ______________________________________________________________________ + + 3.1.1. AIX: General + + [ [159]Top ] [ [160]Contents ] [ [161]Section Contents ] [ [162]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 + + [ [163]Top ] [ [164]Contents ] [ [165]Section Contents ] [ [166]Next ] + [ [167]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 [168]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 + + [ [169]Top ] [ [170]Contents ] [ [171]Section Contents ] [ [172]Next ] + [ [173]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: + [174]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 + + [ [175]Top ] [ [176]Contents ] [ [177]Section Contents ] [ [178]Next ] + [ [179]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) + ______________________________________________________________________ + + 3.1.5. AIX: Xterm Key Map + + [ [180]Top ] [ [181]Contents ] [ [182]Section Contents ] [ + [183]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 + + [ [184]Top ] [ [185]Contents ] [ [186]Section Contents ] [ [187]Next ] + [ [188]Previous ] + + SECTION CONTENTS + + 3.2.0. [189]Common Problems + 3.2.1. [190]Building C-Kermit on HP-UX + 3.2.2. [191]File Transfer + 3.2.3. [192]Dialing Out and UUCP Lockfiles in HP-UX + 3.2.4. [193]Notes on Specific HP-UX Releases + 3.2.5. [194]HP-UX and X.25 + + REFERENCES + + For further information, read the [195]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 + + [ [196]Top ] [ [197]Contents ] [ [198]Section Contents ] [ [199]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 + + [ [200]Top ] [ [201]Contents ] [ [202]Section Contents ] [ [203]Next ] + [ [204]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 [205]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 + + [ [206]Top ] [ [207]Contents ] [ [208]Section Contents ] [ [209]Next ] + [ [210]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 + + [ [211]Top ] [ [212]Contents ] [ [213]Section Contents ] [ [214]Next ] + [ [215]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. [216]HP-UX 11 + 3.2.4.2. [217]HP-UX 10 + 3.2.4.3. [218]HP-UX 9 + 3.2.4.4. [219]HP-UX 8 + 3.2.4.5. [220]HP-UX 7 and Earlier + + 3.2.4.1. HP-UX 11 + + [ [221]Top ] [ [222]Contents ] [ [223]Section Contents ] [ [224]Next ] + + As noted in [225]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 + + [ [226]Top ] [ [227]Contents ] [ [228]Section Contents ] [ [229]Next ] + [ [230]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 + + [ [231]Top ] [ [232]Contents ] [ [233]Section Contents ] [ [234]Next ] + [ [235]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 + + [ [236]Top ] [ [237]Contents ] [ [238]Section Contents ] [ [239]Next ] + [ [240]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 transver 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 transvers 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 availabel from HP's patch base. + ________________________________________________________________________ + + 3.2.4.5. HP-UX 7 and Earlier + + [ [241]Top ] [ [242]Contents ] [ [243]Section Contents ] [ + [244]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 + + [ [245]Top ] [ [246]Contents ] [ [247]Section Contents ] [ + [248]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 + + [ [249]Top ] [ [250]Contents ] [ [251]Section Contents ] [ [252]Next ] + [ [253]Previous ] + + SECTION CONTENTS + + 3.3.1. [254]Problems Building C-Kermit for Linux + 3.3.2. [255]Problems with Serial Devices in Linux + 3.3.3. [256]Terminal Emulation in Linux + 3.3.4. [257]Dates and Times + 3.3.5. [258]Startup Errors + 3.3.6. [259]The Fullscreen File Transfer Display + + REFERENCES + + For further information, read the [260]comp.os.linux.misc, + [261]comp.os.linux.answers, and other Linux-oriented newsgroups, and + see: + + The Linux Document Project (LDP) + [262]http://www.tldp.org/ + + The Linux FAQ + [263]http://www.tldp.org/FAQ/Linux-FAQ.html + + The Linux HOWTOs (especially the Serial HOWTO) + + [264]http://www.tldp.org/HOWTO/Serial-HOWTO.html + + [265]http://tldp.org/HOWTO/Modem-HOWTO.html + + [266]ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO + + [267]ftp://tsx-11.mit.edu/pub/linux/docs/HOWTO + + [268]http://www.tldp.org/HOWTO/ + + [269]http://www.tldp.org/hmirrors.html + + Linux Vendor Tech Support Pages: + + [270]http://www.redhat.com/apps/support/ + + [271]http://www.debian.org/support + + [272]http://www.slackware.com/support/ + + [273]http://www.caldera.com/support/ + + [274]http://www.suse.com/support/ + + [275]http://www.mandrake.com/support/ + + [276]http://www.turbolinux.com/support/ + + Linux Winmodem Support + [277]http://www.linmodems.org/ + + Also see general comments on PC-based Unixes in [278]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: + + [279]http://linux.dreamtime.org/decnet/ + + (But there is no support for it in C-Kermit -- anybody interested in + adding it, please [280]let us 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 [281]rare cases where Linux drivers have + been written for them. See [282]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 [283]secure Telnet clients and servers + have been available for years, and these are more secure than SSH + (for reasons explained [284]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 + + [ [285]Top ] [ [286]Contents ] [ [287]Section Contents ] [ [288]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 + + [ [289]Top ] [ [290]Contents ] [ [291]Section Contents ] [ [292]Next ] + [ [293]Previous ] + + Also see: "man setserial", "man irqtune". + And: [294]Sections 3.0, [295]6, [296]7, and [297]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 [298]10 and [299]11 of the + [300]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 beleive + 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 + manullay 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 [301]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): + + [302]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 + + [ [303]Top ] [ [304]Contents ] [ [305]Section Contents ] [ [306]Next ] + [ [307]Previous ] + + C-Kermit is not a terminal emulator. For a brief explanation of why + not, see [308]Section 3.0.5. For a fuller explanation, [309]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: + + [310]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 [311]Xfree86 xterm, + [312]CLICK HERE. + ________________________________________________________________________ + + 3.3.4. Dates and Times + + [ [313]Top ] [ [314]Contents ] [ [315]Section Contents ] [ [316]Next ] + [ [317]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 indpendently. + 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 + + [ [318]Top ] [ [319]Contents ] [ [320]Section Contents ] [ [321]Next ] + [ [322]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 [323]C-Kermit website for a newer + version. If you have the latest version, then [324]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. + + [325]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 + [326]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 + + [ [327]Top ] [ [328]Contents ] [ [329]Section Contents ] [ + [330]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 + + [ [331]Top ] [ [332]Contents ] [ [333]Section Contents ] [ [334]Next ] + [ [335]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 + + [ [336]Top ] [ [337]Contents ] [ [338]Section Contents ] [ [339]Next ] + [ [340]Previous ] + + See also: The [341]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 [342]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 [343]UUCP + lockfile section of the [344]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 + + [ [345]Top ] [ [346]Contents ] [ [347]Section Contents ] [ [348]Next ] + [ [349]Previous ] + + SECTION CONTENTS + +3.6.1. [350]SCO XENIX +3.6.2. [351]SCO UNIX and OSR5 +3.6.3. [352]Unixware +3.6.4. [353]Open UNIX 8 + + REFERENCES + + * The comp.unix.sco.* newsgroups. + * [354]Section 3.10 below for Unixware. + * The following FAQs: + + The comp.sco.misc FAQ: + [355]http://aplawrence.com/SCOFAQ/ + + Caldera (SCO) comp.unix.sco.programmer FAQ: + [356]http://www.zenez.com/cgi-bin/scoprogfaq/faq.pl + + The UnixWare 7/OpenUNIX 8 FAQ: + [357]http://www.zenez.com/cgi-bin/scouw7faq/faq.pl + [358]http://zenez.pcunix.com/cgi-bin/scouw7faq/faq.pl + + High Speed Modems for SCO Unix: + [359]http://pcunix.com/Unixart/modems.html + + The UnixWare FAQ + [360]http://www.freebird.org/faq/ + + The UnixWare 1.x and 2.0 Programmer FAQ + [361]http://www.freebird.org/faq/developer.html + + Caldera Support Knowledge Base + [362]http://support.caldera.com/caldera + + [363]http://stage.caldera.com/ta/ + Caldera (SCO) Technical Article Search Center + + [364]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 [365]Section 3.0.5. For a fuller + explanation, [366]ClICK HERE. + + Also see general comments on PC-based Unixes in [367]Section 3.0. + + 3.6.1. SCO XENIX + + [ [368]Top ] [ [369]Contents ] [ [370]Section Contents ] [ [371]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 [372]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 + + [ [373]Top ] [ [374]Contents ] [ [375]Section Contents ] [ [376]Next ] + [ [377]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 + implmented, 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., [378]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: + + [379]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 [380]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 + [381]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 [382]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 + + [ [383]Top ] [ [384]Contents ] [ [385]Section Contents ] [ [386]Next ] + [ [387]Previous ] + + Unixware changed hands several times before landing at SCO, and so has + its [388]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 + + [ [389]Top ] [ [390]Contents ] [ [391]Section Contents ] [ + [392]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 + + [ [393]Top ] [ [394]Contents ] [ [395]Section Contents ] [ [396]Next ] + [ [397]Previous ] + + SECTION CONTENTS + +3.7.1. [398]Serial Port Configuration +3.7.2. [399]Serial Port Problems +3.7.3. [400]SunLink X.25 +3.7.4. [401]Sun Workstation Keyboard Mapping +3.7.5. [402]Solaris 2.4 and Earlier + + REFERENCES + + * The [403]comp.unix.solaris newsgroup + * [404]http://access1.sun.com/ + * [405]http://docs.sun.com/ + * [406]http://www.sunhelp.com/ + * [407]http://www.wins.uva.nl/pub/solaris/solaris2/ + * [408]http://www.wins.uva.nl/cgi-bin/sfaq.cgi + * [409]ftp://ftp.wins.uva.nl/pub/solaris + * [410]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": + + [411]http://www.stokely.com/ + + In particular: + + [412]http://www.stokely.com/unix.sysadm.resources/faqs.sun.html + + For PC-based Solaris, also see general comments on PC-based Unixes in + [413]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 + + [ [414]Top ] [ [415]Contents ] [ [416]Section Contents ] [ + [417]Section Contents ] [ [418]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 + + [ [419]Top ] [ [420]Contents ] [ [421]Section Contents ] [ [422]Next ] + [ [423]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 + + [ [424]Top ] [ [425]Contents ] [ [426]Section Contents ] [ [427]Next ] + [ [428]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 + + [ [429]Top ] [ [430]Contents ] [ [431]Section Contents ] [ [432]Next ] + [ [433]Previous ] + + Hints for using a Sun workstation keyboard for VT emulation when + accessing VMS, from the [434]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 + + [ [435]Top ] [ [436]Contents ] [ [437]Section Contents ] [ [438]Next ] + [ [439]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 priviledged + 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 + + [ [440]Top ] [ [441]Contents ] [ [442]Section Contents ] [ + [443]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 + + [ [444]Top ] [ [445]Contents ] [ [446]Section Contents ] [ [447]Next ] + [ [448]Previous ] + + For additional information, see "Celeste's Tutorial on SunOS 4.1.3+ + Modems and Terminals": + + [449]http://www.stokely.com/ + + For FAQs, etc, from Sun, see: + * [450]http://access1.sun.com/ + + For history of Sun models and SunOS versions, see (should be all the + same): + * [451]http://www.ludd.luth.se/~bear/project/sun/sun.hardware.txt + * [452]ftp://ftp.netcom.com/pub/ru/rubicon/sun.hdwr.ref + * [453]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 + + [ [454]Top ] [ [455]Contents ] [ [456]Section Contents ] [ [457]Next ] + [ [458]Previous ] + + See also: The [459]comp.unix.ultrix and [460]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 + + [ [461]Top ] [ [462]Contents ] [ [463]Section Contents ] [ [464]Next ] + [ [465]Previous ] + + See also: + * The Freebird Project (Unixware software repository) + [466]http://www.freebird.org/ + * The UnixWare FAQ: [467]http://www.freebird.org/faq/ + * The following newsgroups: + + [468]comp.unix.unixware.misc + + [469]comp.unix.sco.misc. + + Also see general comments on PC-based Unixes in [470]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 + [471]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 + + [ [472]Top ] [ [473]Contents ] [ [474]Section Contents ] [ [475]Next ] + [ [476]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 + + [ [477]Top ] [ [478]Contents ] [ [479]Section Contents ] [ [480]Next ] + [ [481]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) + + [ [482]Top ] [ [483]Contents ] [ [484]Section Contents ] [ [485]Next ] + [ [486]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. + + Reportedy 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 + + [ [487]Top ] [ [488]Contents ] [ [489]Section Contents ] [ [490]Next ] + [ [491]Previous ] + + See also: + * The [492]comp.sys.sgi.misc and [493]comp.sys.sgi.admin newsgroups. + [494]The SGI website + * The SGI FAQ: + + [495]http://www-viz.tamu.edu/~sgi-faq/ + + [496]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 Inferfaces (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 [497]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: + + [498]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 + [499]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 + + [ [500]Top ] [ [501]Contents ] [ [502]Section Contents ] [ [503]Next ] + [ [504]Previous ] + + See also: The [505]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 + + [ [506]Top ] [ [507]Contents ] [ [508]Section Contents ] [ [509]Next ] + [ [510]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 + + [ [511]Top ] [ [512]Contents ] [ [513]Section Contents ] [ [514]Next ] + [ [515]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 {FREE,OPEN,NET}BSD + + [ [516]Top ] [ [517]Contents ] [ [518]Section Contents ] [ [519]Next ] + [ [520]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 (Rhapsody, Darwin, Jaguar, Panther) + + [ [521]Top ] [ [522]Contents ] [ [523]Section Contents ] [ [524]Next ] + [ [525]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. As far as I can tell, + there is no way to find out the Mac OS X version number, such as 10.3 + (in Linux you can find the distribution version in a + distribution-dependent file). Here are some points to be aware of: + + * The biggest 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, some 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. + * At least early versions of Mac OS X came without Curses, Termlib, + or Terminfo libraries. Later versions seem to have ncurses. 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. The UFS file system + is, like normal Unix, case-sensitive. + * 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. + * 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 has its own package format for applications, 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 modifed (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, 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 [526]Keyspan USB Serial Adapter. To + use the Keyspan device, you must install the accompanying device + drivers, which wind up giving you serial ports with names like + /dev/cu.USA19H3b1P1.1. + + To configure your Mac OS X system to allow C-Kermit to use these (or + any other) serial devices: + + 1. su + 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/bin/kermit + + (or whatever spot is more appropriate). For greater detail about + installation (man page, etc), [527]CLICK HERE. + + 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. The Keyspan USB + adapter 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 essense, 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 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. + + Instructions for the built-in modem remain to be written. + + Links: + * [528]Unix tips for Mac OS X (Jerry Stratton) + ________________________________________________________________________ + + 3.20. C-KERMIT AND COHERENT + + [ [529]Top ] [ [530]Contents ] [ [531]Section Contents ] [ + [532]Previous ] + + Also see: + + [533]http://www.uni-giessen.de/faq/archiv/coherent-faq.general/msg00 + 000.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 + + [ [534]Top ] [ [535]Contents ] [ [536]Next ] [ [537]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 [538]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 attemped 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 + + [ [539]Top ] [ [540]Contents ] [ [541]Next ] [ [542]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 [543]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 + + [ [544]Top ] [ [545]Contents ] [ [546]Next ] [ [547]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 [548]Section 9.5 of the Installation + Instructions, plus any platform-specific notes in [549]Section 3 + above. + ________________________________________________________________________ + + 7. COMMUNICATIONS AND DIALING + + [ [550]Top ] [ [551]Contents ] [ [552]Next ] [ [553]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 [554]Section 8 of the + [555]Installation Instructions. + 7. Read pages 50-67 of [556]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 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 + + [ [557]Top ] [ [558]Contents ] [ [559]Next ] [ [560]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 + + [ [561]Top ] [ [562]Contents ] [ [563]Next ] [ [564]Previous ] + + C-Kermit is not a terminal emulator. Refer to page 147 of [565]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: + + [566]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 + + [ [567]Top ] [ [568]Contents ] [ [569]Next ] [ [570]Previous ] + + 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 constently 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 verions 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 + + [ [571]Top ] [ [572]Contents ] [ [573]Next ] [ [574]Previous ] + + SECTION CONTENTS + + 11.1. [575]C-Kermit as an External Protocol + 11.2. [576]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 + + [ [577]Top ] [ [578]Contents ] [ [579]Section Contents ] [ [580]Next ] + + (This section deleted; see [581]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 + + [ [582]Top ] [ [583]Contents ] [ [584]Section Contents ] [ + [585]Previous ] + + (This section is obsolete, but not totally useless. See Chapter 14 + of [586]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 + + [ [587]Top ] [ [588]Contents ] [ [589]Next ] [ [590]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 [591]security document for details. + ________________________________________________________________________ + + 13. MISCELLANEOUS USER REPORTS + + [ [592]Top ] [ [593]Contents ] [ [594]Next ] [ [595]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 peoblem: +> 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 + + [ [596]Top ] [ [597]Contents ] [ [598]Next ] [ [599]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, [600]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, [601]www.stallion.com, in the + /drivers/ata5/UnixWare directory. + + [ [602]Top ] [ [603]Contents ] [ [604]C-Kermit Home ] [ [605]C-Kermit + 8.0 Overview ] [ [606]Kermit Home ] + _________________________________________________________________ + + C-Kermit 8.0 Unix Hints and Tips / [607]The Kermit Project / + [608]Columbia University / [609]kermit@columbia.edu + +References + + 1. http://www.columbia.edu/kermit/ + 2. http://www.columbia.edu/ + 3. http://www.columbia.edu/kermit/ckubwr.html + 4. mailto:kermit-support@columbia.edu + 5. http://www.columbia.edu/kermit/ckermit.html + 6. http://www.columbia.edu/kermit/ckuins.html + 7. http://www.columbia.edu/kermit/ckututor.html + 8. http://www.columbia.edu/kermit/ckubwr.html#x1 + 9. http://www.columbia.edu/kermit/ckubwr.html#x2 + 10. http://www.columbia.edu/kermit/ckubwr.html#x3 + 11. http://www.columbia.edu/kermit/ckubwr.html#x4 + 12. http://www.columbia.edu/kermit/ckubwr.html#x5 + 13. http://www.columbia.edu/kermit/ckubwr.html#x6 + 14. http://www.columbia.edu/kermit/ckubwr.html#x7 + 15. http://www.columbia.edu/kermit/ckubwr.html#x8 + 16. http://www.columbia.edu/kermit/ckubwr.html#x9 + 17. http://www.columbia.edu/kermit/ckubwr.html#x10 + 18. http://www.columbia.edu/kermit/ckubwr.html#x11 + 19. http://www.columbia.edu/kermit/ckubwr.html#x12 + 20. http://www.columbia.edu/kermit/ckubwr.html#x13 + 21. http://www.columbia.edu/kermit/ckubwr.html#x14 + 22. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 23. http://www.columbia.edu/kermit/ckubwr.html#x3.18 + 24. http://www.columbia.edu/kermit/ckubwr.html#x3.19 + 25. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 26. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 27. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 28. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 29. http://www.columbia.edu/kermit/ckubwr.html#x3.13 + 30. http://www.columbia.edu/kermit/ckubwr.html#top + 31. http://www.columbia.edu/kermit/ckubwr.html#contents + 32. http://www.columbia.edu/kermit/ckubwr.html#x2 + 33. http://www.columbia.edu/kermit/ckubwr.html#x1.1 + 34. http://www.columbia.edu/kermit/ckubwr.html#x1.2 + 35. http://www.columbia.edu/kermit/ckubwr.html#x1.3 + 36. http://www.columbia.edu/kermit/ckubwr.html#x1.4 + 37. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 38. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 39. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 40. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 41. http://www.columbia.edu/kermit/ckcbwr.html + 42. mailto:kermit-support@columbia.edu + 43. http://www.columbia.edu/kermit/ckubwr.html#top + 44. http://www.columbia.edu/kermit/ckubwr.html#contents + 45. http://www.columbia.edu/kermit/ckubwr.html#x1.2 + 46. http://www.columbia.edu/kermit/ck60manual.html + 47. http://www.columbia.edu/kermit/ckermit70.html + 48. http://www.columbia.edu/kermit/ckermit80.html + 49. http://www.columbia.edu/kermit/ckubwr.html#top + 50. http://www.columbia.edu/kermit/ckubwr.html#contents + 51. http://www.columbia.edu/kermit/ckubwr.html#x1 + 52. http://www.columbia.edu/kermit/ckubwr.html#x1.3 + 53. http://www.columbia.edu/kermit/ckubwr.html#x1.1 + 54. http://www.columbia.edu/kermit/support.html + 55. http://www.columbia.edu/kermit/ckubwr.html#top + 56. http://www.columbia.edu/kermit/ckubwr.html#contents + 57. http://www.columbia.edu/kermit/ckubwr.html#x1 + 58. http://www.columbia.edu/kermit/ckubwr.html#x1.4 + 59. http://www.columbia.edu/kermit/ckubwr.html#x1.2 + 60. http://www.columbia.edu/kermit/ckubwr.html#top + 61. http://www.columbia.edu/kermit/ckubwr.html#contents + 62. http://www.columbia.edu/kermit/ckubwr.html#x1 + 63. http://www.columbia.edu/kermit/ckubwr.html#x1.3 + 64. http://www.columbia.edu/kermit/ckubwr.html#top + 65. http://www.columbia.edu/kermit/ckubwr.html#contents + 66. http://www.columbia.edu/kermit/ckubwr.html#x3 + 67. http://www.columbia.edu/kermit/ckubwr.html#x1 + 68. http://www.columbia.edu/kermit/ckubwr.html#top + 69. http://www.columbia.edu/kermit/ckubwr.html#contents + 70. http://www.columbia.edu/kermit/ckubwr.html#x4 + 71. http://www.columbia.edu/kermit/ckubwr.html#x2 + 72. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 73. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 74. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 75. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 76. http://www.columbia.edu/kermit/ckubwr.html#x3.4 + 77. http://www.columbia.edu/kermit/ckubwr.html#x3.5 + 78. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 79. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 80. http://www.columbia.edu/kermit/ckubwr.html#x3.8 + 81. http://www.columbia.edu/kermit/ckubwr.html#x3.9 + 82. http://www.columbia.edu/kermit/ckubwr.html#x3.10 + 83. http://www.columbia.edu/kermit/ckubwr.html#x3.11 + 84. http://www.columbia.edu/kermit/ckubwr.html#x3.12 + 85. http://www.columbia.edu/kermit/ckubwr.html#x3.13 + 86. http://www.columbia.edu/kermit/ckubwr.html#x3.14 + 87. http://www.columbia.edu/kermit/ckubwr.html#x3.15 + 88. http://www.columbia.edu/kermit/ckubwr.html#x3.16 + 89. http://www.columbia.edu/kermit/ckubwr.html#x3.17 + 90. http://www.columbia.edu/kermit/ckubwr.html#x3.18 + 91. http://www.columbia.edu/kermit/ckubwr.html#x3.19 + 92. http://www.columbia.edu/kermit/ckubwr.html#x3.20 + 93. http://www.faqs.org/ + 94. http://aplawrence.com/Unixart/newtounix.html + 95. http://www.columbia.edu/kermit/x3 + 96. mailto:kermit-support@columbia.edu + 97. http://www.columbia.edu/kermit/support.html + 98. http://www.columbia.edu/kermit/ckubwr.html#top + 99. http://www.columbia.edu/kermit/ckubwr.html#contents + 100. http://www.columbia.edu/kermit/ckubwr.html#x3 + 101. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 102. http://www.pcunix.com/ + 103. http://www.columbia.edu/kermit/ckubwr.html#x3.0.1 + 104. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 + 105. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 + 106. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 + 107. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 + 108. http://www.columbia.edu/kermit/ckubwr.html#x3.0.6 + 109. http://www.columbia.edu/kermit/ckubwr.html#top + 110. http://www.columbia.edu/kermit/ckubwr.html#contents + 111. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 112. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 + 113. http://www.columbia.edu/kermit/ckubwr.html#top + 114. http://www.columbia.edu/kermit/ckubwr.html#contents + 115. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 116. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 + 117. http://www.columbia.edu/kermit/ckubwr.html#x3.0.1 + 118. http://www.linmodems.org/ + 119. http://www.microsoft.com/hwdev/platform/PCdesign/LR/default.asp + 120. http://www.columbia.edu/kermit/ckubwr.html#top + 121. http://www.columbia.edu/kermit/ckubwr.html#contents + 122. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 123. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 + 124. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 + 125. http://www.idir.net/~gromitkc/winmodem.html + 126. http://www.digi.com/ + 127. http://www.columbia.edu/kermit/ckubwr.html#top + 128. http://www.columbia.edu/kermit/ckubwr.html#contents + 129. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 130. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 + 131. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 + 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.6 + 136. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 + 137. http://www.columbia.edu/kermit/ckubwr.html#top + 138. http://www.columbia.edu/kermit/ckubwr.html#contents + 139. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 140. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 + 141. http://www.columbia.edu/kermit/ckubwr.html#top + 142. http://www.columbia.edu/kermit/ckubwr.html#contents + 143. http://www.columbia.edu/kermit/ckubwr.html#x3 + 144. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 145. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 146. http://www.columbia.edu/kermit/ckubwr.html#x3.1.1 + 147. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 + 148. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 + 149. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 + 150. http://www.columbia.edu/kermit/ckubwr.html#x3.1.5 + 151. http://www.emerson.emory.edu/services/aix-faq/ + 152. http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.aix.html + 153. http://www.cis.ohio-state.edu/hypertext/faq/usenet/aix-faq/top.html + 154. http://aixpdslib.seas.ucla.edu/ + 155. http://www.rootvg.net (AIX history)/ + 156. ftp://rtfm.mit.edu/pub/usenet/news.answers/aix-faq/part1 + 157. ftp://mirrors.aol.com/pub/rtfm/usenet-by-hierarchy/comp/unix/aix + 158. news:comp.unix.aix + 159. http://www.columbia.edu/kermit/ckubwr.html#top + 160. http://www.columbia.edu/kermit/ckubwr.html#contents + 161. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 162. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 + 163. http://www.columbia.edu/kermit/ckubwr.html#top + 164. http://www.columbia.edu/kermit/ckubwr.html#contents + 165. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 166. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 + 167. http://www.columbia.edu/kermit/ckubwr.html#x3.1.1 + 168. http://www.columbia.edu/kermit/security.html#servers + 169. http://www.columbia.edu/kermit/ckubwr.html#top + 170. http://www.columbia.edu/kermit/ckubwr.html#contents + 171. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 172. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 + 173. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 + 174. http://service.software.ibm.com/rs6000/ + 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.5 + 179. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 + 180. http://www.columbia.edu/kermit/ckubwr.html#top + 181. http://www.columbia.edu/kermit/ckubwr.html#contents + 182. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 183. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 + 184. http://www.columbia.edu/kermit/ckubwr.html#top + 185. http://www.columbia.edu/kermit/ckubwr.html#contents + 186. http://www.columbia.edu/kermit/ckubwr.html#x3 + 187. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 188. http://www.columbia.edu/kermit/ckubwr.html#x3.1 + 189. http://www.columbia.edu/kermit/ckubwr.html#x3.2.0 + 190. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 + 191. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 + 192. http://www.columbia.edu/kermit/ckubwr.html#x3.2.3 + 193. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 194. http://www.columbia.edu/kermit/ckubwr.html#x3.2.5 + 195. news:comp.sys.hp.hpux + 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.2 + 199. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 + 200. http://www.columbia.edu/kermit/ckubwr.html#top + 201. http://www.columbia.edu/kermit/ckubwr.html#contents + 202. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 203. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 + 204. http://www.columbia.edu/kermit/ckubwr.html#x3.2.0 + 205. ftp://kermit.columbia.edu/kermit/f/makefile + 206. http://www.columbia.edu/kermit/ckubwr.html#top + 207. http://www.columbia.edu/kermit/ckubwr.html#contents + 208. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 209. http://www.columbia.edu/kermit/ckubwr.html#x3.2.3 + 210. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 + 211. http://www.columbia.edu/kermit/ckubwr.html#top + 212. http://www.columbia.edu/kermit/ckubwr.html#contents + 213. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 214. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 215. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 + 216. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.1 + 217. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 + 218. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 + 219. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 + 220. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.5 + 221. http://www.columbia.edu/kermit/ckubwr.html#top + 222. http://www.columbia.edu/kermit/ckubwr.html#contents + 223. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 224. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 + 225. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 + 226. http://www.columbia.edu/kermit/ckubwr.html#top + 227. http://www.columbia.edu/kermit/ckubwr.html#contents + 228. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 229. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 + 230. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.1 + 231. http://www.columbia.edu/kermit/ckubwr.html#top + 232. http://www.columbia.edu/kermit/ckubwr.html#contents + 233. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 234. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 + 235. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 + 236. http://www.columbia.edu/kermit/ckubwr.html#top + 237. http://www.columbia.edu/kermit/ckubwr.html#contents + 238. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 239. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.5 + 240. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 + 241. http://www.columbia.edu/kermit/ckubwr.html#top + 242. http://www.columbia.edu/kermit/ckubwr.html#contents + 243. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 244. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 + 245. http://www.columbia.edu/kermit/ckubwr.html#top + 246. http://www.columbia.edu/kermit/ckubwr.html#contents + 247. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 248. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 + 249. http://www.columbia.edu/kermit/ckubwr.html#top + 250. http://www.columbia.edu/kermit/ckubwr.html#contents + 251. http://www.columbia.edu/kermit/ckubwr.html#x3 + 252. http://www.columbia.edu/kermit/ckubwr.html#x3.4 + 253. http://www.columbia.edu/kermit/ckubwr.html#x3.2 + 254. http://www.columbia.edu/kermit/ckubwr.html#x3.3.1 + 255. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 + 256. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 + 257. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 + 258. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 + 259. http://www.columbia.edu/kermit/ckubwr.html#x3.3.6 + 260. news:comp.os.linux.misc + 261. news:comp.os.linux.answers + 262. http://www.tldp.org/ + 263. http://www.tldp.org/FAQ/Linux-FAQ.html + 264. http://www.tldp.org/HOWTO/Serial-HOWTO.html + 265. http://tldp.org/HOWTO/Modem-HOWTO.html + 266. ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO + 267. ftp://tsx-11.mit.edu/pub/linux/docs/HOWTO + 268. http://www.tldp.org/HOWTO/ + 269. http://www.tldp.org/hmirrors.html + 270. http://www.redhat.com/apps/support/ + 271. http://www.debian.org/support + 272. http://www.slackware.com/support/ + 273. http://www.caldera.com/support/ + 274. http://www.suse.com/support/ + 275. http://www.mandrake.com/support/ + 276. http://www.turbolinux.com/support/ + 277. http://www.linmodems.org/ + 278. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 279. http://linux.dreamtime.org/decnet/ + 280. mailto:kermit-support@columbia.edu + 281. http://www.linmodems.org/ + 282. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 + 283. http://www.columbia.edu/kermit/security.html#servers + 284. http://www.columbia.edu/kermit/sshclient.html + 285. http://www.columbia.edu/kermit/ckubwr.html#top + 286. http://www.columbia.edu/kermit/ckubwr.html#contents + 287. http://www.columbia.edu/kermit/ckubwr.html#x3 + 288. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 + 289. http://www.columbia.edu/kermit/ckubwr.html#top + 290. http://www.columbia.edu/kermit/ckubwr.html#contents + 291. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 292. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 + 293. http://www.columbia.edu/kermit/ckubwr.html#x3.3.1 + 294. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 295. http://www.columbia.edu/kermit/ckubwr.html#x6 + 296. http://www.columbia.edu/kermit/ckubwr.html#x7 + 297. http://www.columbia.edu/kermit/ckubwr.html#x8 + 298. http://www.columbia.edu/kermit/ckuins.html#x10 + 299. http://www.columbia.edu/kermit/ckuins.html#x11 + 300. http://www.columbia.edu/kermit/ckuins.html + 301. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 302. http://linuxwww.db.erau.edu/mail_archives/linux-kernel/Mar_98/1441.html + 303. http://www.columbia.edu/kermit/ckubwr.html#top + 304. http://www.columbia.edu/kermit/ckubwr.html#contents + 305. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 306. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 + 307. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 + 308. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 + 309. http://www.columbia.edu/kermit/ckfaq.html#term + 310. http://dickey.his.com/xterm/xterm.html + 311. http://dickey.his.com/xterm/xterm.html + 312. ftp://kermit.columbia.edu/kermit/f/xmodmap.txt + 313. http://www.columbia.edu/kermit/ckubwr.html#top + 314. http://www.columbia.edu/kermit/ckubwr.html#contents + 315. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 316. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 + 317. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 + 318. http://www.columbia.edu/kermit/ckubwr.html#top + 319. http://www.columbia.edu/kermit/ckubwr.html#contents + 320. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 321. http://www.columbia.edu/kermit/ckubwr.html#x3.3.6 + 322. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 + 323. http://www.columbia.edu/kermit/ckermit.html + 324. mailto:kermit-support@columbia.edu + 325. http://www.redhat.com/support/errata/RHBA-2001-153.html + 326. news:comp.protocols.kermit.misc + 327. http://www.columbia.edu/kermit/ckubwr.html#top + 328. http://www.columbia.edu/kermit/ckubwr.html#contents + 329. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 330. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 + 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 + 334. http://www.columbia.edu/kermit/ckubwr.html#x3.5 + 335. http://www.columbia.edu/kermit/ckubwr.html#x3.3 + 336. http://www.columbia.edu/kermit/ckubwr.html#top + 337. http://www.columbia.edu/kermit/ckubwr.html#contents + 338. http://www.columbia.edu/kermit/ckubwr.html#x3 + 339. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 340. http://www.columbia.edu/kermit/ckubwr.html#x3.4 + 341. news:comp.os.qnx + 342. http://www.columbia.edu/kermit/gkermit.html + 343. http://www.columbia.edu/kermit/ckuins.html#x10 + 344. http://www.columbia.edu/kermit/ckuins.html + 345. http://www.columbia.edu/kermit/ckubwr.html#top + 346. http://www.columbia.edu/kermit/ckubwr.html#contents + 347. http://www.columbia.edu/kermit/ckubwr.html#x3 + 348. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 349. http://www.columbia.edu/kermit/ckubwr.html#x3.5 + 350. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 + 351. http://www.columbia.edu/kermit/ckubwr.html#x3.6.2 + 352. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 + 353. http://www.columbia.edu/kermit/ckubwr.html#x3.6.4 + 354. http://www.columbia.edu/kermit/ckubwr.html#x3.10 + 355. http://aplawrence.com/SCOFAQ/ + 356. http://www.zenez.com/cgi-bin/scoprogfaq/faq.pl + 357. http://www.zenez.com/cgi-bin/scouw7faq/faq.pl + 358. http://zenez.pcunix.com/cgi-bin/scouw7faq/faq.pl + 359. http://pcunix.com/Unixart/modems.html + 360. http://www.freebird.org/faq/ + 361. http://www.freebird.org/faq/developer.html + 362. http://support.caldera.com/caldera + 363. http://stage.caldera.com/ta/ + 364. http://aplawrence.com/newtosco.html + 365. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 + 366. http://www.columbia.edu/kermit/ckfaq.html#term + 367. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 368. http://www.columbia.edu/kermit/ckubwr.html#top + 369. http://www.columbia.edu/kermit/ckubwr.html#contents + 370. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 371. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 + 372. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 373. http://www.columbia.edu/kermit/ckubwr.html#top + 374. http://www.columbia.edu/kermit/ckubwr.html#contents + 375. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 376. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 + 377. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 + 378. http://www.digi.com/ + 379. ftp://ftp.fu-berlin.de/pub/unix/driver/fas + 380. http://www.columbia.edu/kermit/ckubwr.html#x14 + 381. http://www.sco.com/ + 382. ftp://ftp.sco.com/ + 383. http://www.columbia.edu/kermit/ckubwr.html#top + 384. http://www.columbia.edu/kermit/ckubwr.html#contents + 385. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 386. http://www.columbia.edu/kermit/ckubwr.html#x3.6.4 + 387. http://www.columbia.edu/kermit/ckubwr.html#x3.6.2 + 388. http://www.columbia.edu/kermit/ckubwr.html#x3.10 + 389. http://www.columbia.edu/kermit/ckubwr.html#top + 390. http://www.columbia.edu/kermit/ckubwr.html#contents + 391. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 392. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 + 393. http://www.columbia.edu/kermit/ckubwr.html#top + 394. http://www.columbia.edu/kermit/ckubwr.html#contents + 395. http://www.columbia.edu/kermit/ckubwr.html#x3 + 396. http://www.columbia.edu/kermit/ckubwr.html#x3.8 + 397. http://www.columbia.edu/kermit/ckubwr.html#x3.6 + 398. http://www.columbia.edu/kermit/ckubwr.html#x3.7.1 + 399. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 + 400. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 + 401. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 + 402. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 + 403. news:comp.unix.solaris + 404. http://access1.sun.com/ + 405. http://docs.sun.com/ + 406. http://www.sunhelp.com/ + 407. http://www.wins.uva.nl/pub/solaris/solaris2/ + 408. http://www.wins.uva.nl/cgi-bin/sfaq.cgi + 409. ftp://ftp.wins.uva.nl/pub/solaris + 410. http://www.science.uva.nl/pub/solaris/solaris2.html + 411. http://www.stokely.com/ + 412. http://www.stokely.com/unix.sysadm.resources/faqs.sun.html + 413. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 414. http://www.columbia.edu/kermit/ckubwr.html#top + 415. http://www.columbia.edu/kermit/ckubwr.html#contents + 416. http://www.columbia.edu/kermit/ckubwr.html#x3 + 417. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 418. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 + 419. http://www.columbia.edu/kermit/ckubwr.html#top + 420. http://www.columbia.edu/kermit/ckubwr.html#contents + 421. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 422. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 + 423. http://www.columbia.edu/kermit/ckubwr.html#x3.7.1 + 424. http://www.columbia.edu/kermit/ckubwr.html#top + 425. http://www.columbia.edu/kermit/ckubwr.html#contents + 426. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 427. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 + 428. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 + 429. http://www.columbia.edu/kermit/ckubwr.html#top + 430. http://www.columbia.edu/kermit/ckubwr.html#contents + 431. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 432. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 + 433. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 + 434. news:comp.os.vms + 435. http://www.columbia.edu/kermit/ckubwr.html#top + 436. http://www.columbia.edu/kermit/ckubwr.html#contents + 437. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 438. http://www.columbia.edu/kermit/ckubwr.html#x3.7.6 + 439. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 + 440. http://www.columbia.edu/kermit/ckubwr.html#top + 441. http://www.columbia.edu/kermit/ckubwr.html#contents + 442. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 443. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 + 444. http://www.columbia.edu/kermit/ckubwr.html#top + 445. http://www.columbia.edu/kermit/ckubwr.html#contents + 446. http://www.columbia.edu/kermit/ckubwr.html#x3 + 447. http://www.columbia.edu/kermit/ckubwr.html#x3.9 + 448. http://www.columbia.edu/kermit/ckubwr.html#x3.7 + 449. http://www.stokely.com/ + 450. http://access1.sun.com/ + 451. http://www.ludd.luth.se/~bear/project/sun/sun.hardware.txt + 452. ftp://ftp.netcom.com/pub/ru/rubicon/sun.hdwr.ref + 453. ftp://ftp.intnet.net/pub/SUN/Sun-Hardware-Ref + 454. http://www.columbia.edu/kermit/ckubwr.html#top + 455. http://www.columbia.edu/kermit/ckubwr.html#contents + 456. http://www.columbia.edu/kermit/ckubwr.html#x3 + 457. http://www.columbia.edu/kermit/ckubwr.html#x3.10 + 458. http://www.columbia.edu/kermit/ckubwr.html#x3.8 + 459. news:comp.unix.ultrix + 460. news:comp.sys.dec + 461. http://www.columbia.edu/kermit/ckubwr.html#top + 462. http://www.columbia.edu/kermit/ckubwr.html#contents + 463. http://www.columbia.edu/kermit/ckubwr.html#x3 + 464. http://www.columbia.edu/kermit/ckubwr.html#x3.11 + 465. http://www.columbia.edu/kermit/ckubwr.html#x3.9 + 466. http://www.freebird.org/ + 467. http://www.freebird.org/faq/ + 468. news:comp.unix.unixware.misc + 469. news:comp.unix.sco.misc + 470. http://www.columbia.edu/kermit/ckubwr.html#x3.0 + 471. ftp://kermit.columbia.edu/kermit/f/ckutio.c + 472. http://www.columbia.edu/kermit/ckubwr.html#top + 473. http://www.columbia.edu/kermit/ckubwr.html#contents + 474. http://www.columbia.edu/kermit/ckubwr.html#x3 + 475. http://www.columbia.edu/kermit/ckubwr.html#x3.12 + 476. http://www.columbia.edu/kermit/ckubwr.html#x3.10 + 477. http://www.columbia.edu/kermit/ckubwr.html#top + 478. http://www.columbia.edu/kermit/ckubwr.html#contents + 479. http://www.columbia.edu/kermit/ckubwr.html#x3 + 480. http://www.columbia.edu/kermit/ckubwr.html#x3.13 + 481. http://www.columbia.edu/kermit/ckubwr.html#x3.11 + 482. http://www.columbia.edu/kermit/ckubwr.html#top + 483. http://www.columbia.edu/kermit/ckubwr.html#contents + 484. http://www.columbia.edu/kermit/ckubwr.html#x3 + 485. http://www.columbia.edu/kermit/ckubwr.html#x3.14 + 486. http://www.columbia.edu/kermit/ckubwr.html#x3.12 + 487. http://www.columbia.edu/kermit/ckubwr.html#top + 488. http://www.columbia.edu/kermit/ckubwr.html#contents + 489. http://www.columbia.edu/kermit/ckubwr.html#x3 + 490. http://www.columbia.edu/kermit/ckubwr.html#x3.15 + 491. http://www.columbia.edu/kermit/ckubwr.html#x3.13 + 492. news:comp.sys.sgi.misc + 493. news:comp.sys.sgi.admin + 494. http://www.sgi.com/ + 495. http://www-viz.tamu.edu/~sgi-faq/ + 496. ftp://viz.tamu.edu/pub/sgi/faq/ + 497. http://www.columbia.edu/kermit/ckuins.html + 498. http://freeware.sgi.com/Installable/gcc-2.95.2.html + 499. http://freeware.sgi.com/Installable/gcc-2.95.2.html + 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.16 + 504. http://www.columbia.edu/kermit/ckubwr.html#x3.14 + 505. news:comp.sys.be + 506. http://www.columbia.edu/kermit/ckubwr.html#top + 507. http://www.columbia.edu/kermit/ckubwr.html#contents + 508. http://www.columbia.edu/kermit/ckubwr.html#x3 + 509. http://www.columbia.edu/kermit/ckubwr.html#x3.17 + 510. http://www.columbia.edu/kermit/ckubwr.html#x3.15 + 511. http://www.columbia.edu/kermit/ckubwr.html#top + 512. http://www.columbia.edu/kermit/ckubwr.html#contents + 513. http://www.columbia.edu/kermit/ckubwr.html#x3 + 514. http://www.columbia.edu/kermit/ckubwr.html#x3.18 + 515. http://www.columbia.edu/kermit/ckubwr.html#x3.16 + 516. http://www.columbia.edu/kermit/ckubwr.html#top + 517. http://www.columbia.edu/kermit/ckubwr.html#contents + 518. http://www.columbia.edu/kermit/ckubwr.html#x3 + 519. http://www.columbia.edu/kermit/ckubwr.html#x3.19 + 520. http://www.columbia.edu/kermit/ckubwr.html#x3.17 + 521. http://www.columbia.edu/kermit/ckubwr.html#top + 522. http://www.columbia.edu/kermit/ckubwr.html#contents + 523. http://www.columbia.edu/kermit/ckubwr.html#x3 + 524. http://www.columbia.edu/kermit/ckubwr.html#x3.20 + 525. http://www.columbia.edu/kermit/ckubwr.html#x3.18 + 526. http://www.keyspan.com/products/usb/adapter/ + 527. http://www.columbia.edu/kermit/ckuins.html + 528. http://cerebus.sandiego.edu/~jerry/UnixTips/ + 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.uni-giessen.de/faq/archiv/coherent-faq.general/msg00000.html + 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#x5 + 537. http://www.columbia.edu/kermit/ckubwr.html#x3 + 538. http://www.columbia.edu/kermit/ckccfg.html + 539. http://www.columbia.edu/kermit/ckubwr.html#top + 540. http://www.columbia.edu/kermit/ckubwr.html#contents + 541. http://www.columbia.edu/kermit/ckubwr.html#x6 + 542. http://www.columbia.edu/kermit/ckubwr.html#x4 + 543. http://www.columbia.edu/kermit/ckuins.html + 544. http://www.columbia.edu/kermit/ckubwr.html#top + 545. http://www.columbia.edu/kermit/ckubwr.html#contents + 546. http://www.columbia.edu/kermit/ckubwr.html#x7 + 547. http://www.columbia.edu/kermit/ckubwr.html#x5 + 548. http://www.columbia.edu/kermit/ckuins.html#9.5 + 549. http://www.columbia.edu/kermit/ckubwr.html#x3 + 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#x8 + 553. http://www.columbia.edu/kermit/ckubwr.html#x6 + 554. http://www.columbia.edu/kermit/ckuins.html#x8 + 555. http://www.columbia.edu/kermit/ckuins.html + 556. http://www.columbia.edu/kermit/ck60manual.html + 557. http://www.columbia.edu/kermit/ckubwr.html#top + 558. http://www.columbia.edu/kermit/ckubwr.html#contents + 559. http://www.columbia.edu/kermit/ckubwr.html#x9 + 560. http://www.columbia.edu/kermit/ckubwr.html#x7 + 561. http://www.columbia.edu/kermit/ckubwr.html#top + 562. http://www.columbia.edu/kermit/ckubwr.html#contents + 563. http://www.columbia.edu/kermit/ckubwr.html#x10 + 564. http://www.columbia.edu/kermit/ckubwr.html#x8 + 565. http://www.columbia.edu/kermit/ck60manual.html + 566. http://dickey.his.com/xterm/xterm.html + 567. http://www.columbia.edu/kermit/ckubwr.html#top + 568. http://www.columbia.edu/kermit/ckubwr.html#contents + 569. http://www.columbia.edu/kermit/ckubwr.html#x11 + 570. http://www.columbia.edu/kermit/ckubwr.html#x9 + 571. http://www.columbia.edu/kermit/ckubwr.html#top + 572. http://www.columbia.edu/kermit/ckubwr.html#contents + 573. http://www.columbia.edu/kermit/ckubwr.html#x12 + 574. http://www.columbia.edu/kermit/ckubwr.html#x10 + 575. http://www.columbia.edu/kermit/ckubwr.html#x11.1 + 576. http://www.columbia.edu/kermit/ckubwr.html#x11.2 + 577. http://www.columbia.edu/kermit/ckubwr.html#top + 578. http://www.columbia.edu/kermit/ckubwr.html#contents + 579. http://www.columbia.edu/kermit/ckubwr.html#x11 + 580. http://www.columbia.edu/kermit/ckubwr.html#x11.2 + 581. http://www.columbia.edu/kermit/ck60manual.html + 582. http://www.columbia.edu/kermit/ckubwr.html#top + 583. http://www.columbia.edu/kermit/ckubwr.html#contents + 584. http://www.columbia.edu/kermit/ckubwr.html#x11 + 585. http://www.columbia.edu/kermit/ckubwr.html#x11.1 + 586. http://www.columbia.edu/kermit/ck60manual.html + 587. http://www.columbia.edu/kermit/ckubwr.html#top + 588. http://www.columbia.edu/kermit/ckubwr.html#contents + 589. http://www.columbia.edu/kermit/ckubwr.html#x13 + 590. http://www.columbia.edu/kermit/ckubwr.html#x11 + 591. http://www.columbia.edu/kermit/security.html + 592. http://www.columbia.edu/kermit/ckubwr.html#top + 593. http://www.columbia.edu/kermit/ckubwr.html#contents + 594. http://www.columbia.edu/kermit/ckubwr.html#x14 + 595. http://www.columbia.edu/kermit/ckubwr.html#x12 + 596. http://www.columbia.edu/kermit/ckubwr.html#top + 597. http://www.columbia.edu/kermit/ckubwr.html#contents + 598. http://www.columbia.edu/kermit/ckubwr.html#x15 + 599. http://www.columbia.edu/kermit/ckubwr.html#x14 + 600. mailto:support@stallion.oz.au + 601. http://www.stallion.com/ + 602. http://www.columbia.edu/kermit/ckubwr.html#top + 603. http://www.columbia.edu/kermit/ckubwr.html#contents + 604. http://www.columbia.edu/kermit/ckermit.html + 605. http://www.columbia.edu/kermit/ck80.html + 606. http://www.columbia.edu/kermit/index.html + 607. http://www.columbia.edu/kermit/index.html + 608. http://www.columbia.edu/ + 609. mailto:kermit@columbia.edu diff --git a/ckucmd.c b/ckucmd.c new file mode 100644 index 0000000..e1bca34 --- /dev/null +++ b/ckucmd.c @@ -0,0 +1,7639 @@ +#include "ckcsym.h" + +char *cmdv = "Command package 8.0.157, 11 May 2003"; + +/* C K U C M D -- Interactive command package for Unix */ + +/* + Author: Frank da Cruz (fdc@columbia.edu), + Columbia University Academic Information Systems, New York City. + + Copyright (C) 1985, 2004, + 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; +#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 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 */ + +#include /* Error number symbols */ + +#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 NT +#define stricmp _stricmp +#endif /* NT */ + +#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 */ + +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 */ +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 */ +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 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; +} + +/* 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 + + 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 +filhelp(n,pre,post,off,cmdirflg) int n, off; char *pre, *post; int cmdirflg; { + 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 */ +#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); + } +} + +/* 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 (!(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 */ + debug(F101,"prompt sx","",sx); + debug(F101,"prompt sy","",sy); + 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 +*/ +int +cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *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 = atoi(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 = atoi(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 = atoi(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. +*/ + +/* + 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); +} + +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; + 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 = ""; + + 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 */ + *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); + 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 */ + 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); + 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. */ + y = dirflg ? itsadir : zchki(*xp); /* Now 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); + free(sv); /* done with this */ + sv = NULL; + if (dirflg && y == 0) { + printf("?Not a directory - %s\n",*xp); +#ifdef CKCHANNELIO + z_error = FX_ACC; +#endif /* CKCHANNELIO */ + return(-9); + } else if (y == -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 (y == -2) { + if (!recursive) { + if (np) free(np); + if (d) 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 (y < 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); + 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); + 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 -- processing 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 =) + 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 +*/ +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 { + rtimer(); /* Reset timer */ + zz = gtword((pmsg == 4) ? 1 : 0);/* Otherwise get a command word */ + } + + 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) { + 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 */ + } + if (cmswitch && *atmbuf != '/') { + if (pmsg & 1) { + bleep(BP_FAIL); + printf("?Not a switch - %s\n",atmbuf); + } + cmflgs = -2; + return(-6); + } + 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 = 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 sin","",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) { + mo += (sign * dmo); + if (mo > 12 || mo < 0) { + yy += mo / 12; + mo = mo % 12; + } + } + 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; + } + 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); + } + + /* 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 */ + if (hh > 23) { /* Offset crosses date boundary */ + 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); + } + } + 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 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); +} + +/* 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. + Returns: + Pointer to result if args valid, otherwise original arg pointer. +*/ +char * +shuffledate(p,opt) char * p; int opt; { + 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 > 3) + return(p); + len = strlen(p); + if (len < 8 || len > 31) return(p); + 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); +} + +/* 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, + 0, + NULL, + 0 +}; + +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; + 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 _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->kflags = (in->fcode == _CMKEY) ? cmkwflgs : 0; + debug(F111,"cmfdb out->nresult",out->sresult,out->nresult); + 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 */ + } +} + + +/* 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. + +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 && (!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 (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) { + putchar(c); /* echo it. */ + fflush(stdout); + fflush(stderr); + } +#else + if (echof) { /* echo it. */ + putchar((CHAR)c); + 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) { + putchar(c); /* Echo it. */ + fflush(stdout); + fflush(stderr); + } +#else + if (echof) { + putchar((CHAR)c); + if (timelimit) + fflush(stdout); + } +#endif /* BEBOX */ + if ((*pp != lbrace) || (bracelvl == 0)) { + 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); + } + 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 + ) { + putchar((CHAR)c); + *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++)) { +#ifdef isprint + putchar((CHAR) (isprint(cx) ? cx : '^')); +#else + putchar((CHAR) ((cx >= SP && cx < DEL) ? cx : '^')); +#endif /* isprint */ + } + fflush(stdout); + continue; + } + } + + +#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 + if (echof) 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 */ + 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 */ + if (!dpx) return; + /* 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 */ diff --git a/ckucmd.h b/ckucmd.h new file mode 100644 index 0000000..e994843 --- /dev/null +++ b/ckucmd.h @@ -0,0 +1,302 @@ +/* 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, 2004, + 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; /* Numeric result */ + int kflags; /* Keyword flags if any */ +} 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 */ + +/* Function prototypes */ + +_PROTOTYP( int xxesc, (char **) ); +_PROTOTYP( int cmrini, (int) ); +_PROTOTYP( VOID cmsetp, (char *) ); +_PROTOTYP( VOID cmsavp, (char [], int) ); +_PROTOTYP( char * cmgetp, () ); +_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 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, () ); +#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) ); + +#ifdef DCMDBUF +_PROTOTYP( int cmsetup, (void) ); +#endif /* DCMDBUF */ + +#endif /* CKUCMD_H */ + +/* End of ckucmd.h */ diff --git a/ckucns.c b/ckucns.c new file mode 100644 index 0000000..296b106 --- /dev/null +++ b/ckucns.c @@ -0,0 +1,2679 @@ +#include "ckcsym.h" +char *connv = "CONNECT Command for UNIX:select(), 8.0.135, 29 Nov 2002"; + +/* 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, 2004, + 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, 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 */ + +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 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 translated + into accented characters, 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 */ + +/* 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\r\n",sesfil,s); + } + 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 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 */ +#ifdef XPRINT + escseq |= tt_print; +#endif /* XPRINT */ + inesc[0] = ES_NORMAL; /* Initial state of recognizer */ + inesc[1] = ES_NORMAL; + 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); +#ifndef NOSETKEY + debug(F101,"CONNECT kmptr","",kmptr); +#endif /* NOSETKEY */ + } +#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 + 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 */ + /* debug(F101,"CONNECT c","",c); */ + 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); + while (*s) + ckcputc(*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. */ + 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 (dontprint) { /* Do transparent printing. */ + dontprint = 0; + continue; + } else + + 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 */ diff --git a/ckucon.c b/ckucon.c new file mode 100644 index 0000000..bd866fe --- /dev/null +++ b/ckucon.c @@ -0,0 +1,2679 @@ +#include "ckcsym.h" + +char *connv = "CONNECT Command for UNIX:fork(), 8.0.114, 29 Nov 2002"; + +/* 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, 2004, + 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 */ +#include /* Error numbers */ + +#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, 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 */ + inesc = 0, /* State of sequence recognizer */ + 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 */ + } + 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 */ diff --git a/ckudia.c b/ckudia.c new file mode 100644 index 0000000..2f0fbfe --- /dev/null +++ b/ckudia.c @@ -0,0 +1,8249 @@ +#include "ckcsym.h" +char *dialv = "Dial Command, 8.0.160, 29 Apr 2002"; + +/* C K U D I A -- Module for automatic modem dialing. */ + +/* + Copyright (C) 1985, 2004, + 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 */ diff --git a/ckufio.c b/ckufio.c new file mode 100644 index 0000000..298c48e --- /dev/null +++ b/ckufio.c @@ -0,0 +1,8310 @@ +/* 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, 8.0.200, 4 Mar 2004"; +#else +#ifdef Plan9 +char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004"; +#else +char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004"; +#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. + + Copyright (C) 1985, 2004, + 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 */ + +#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 */ + +#include +#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 */ +#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) ); + +/* + 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... */ +#include +#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 BIGBUFOK +#define SSPACE 500000 +#else +#define SSPACE 10000 +#endif /* BIGBUFOK */ +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 */ + +#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 */ { +#ifdef HAVEUTMPX + struct utmpx ut; /* Needed for ut_host[] */ +#else + struct utmp ut; +#endif /* HAVEUTMPX */ + 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)); + ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); +#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 + time(&ut.ut_time); +#endif /* LINUX */ +#endif /* HAVEUTMPX */ + if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) != + sizeof(struct utmp)) { +#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; + +static long iflen = -1L; /* 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; + +/* 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 DEBUG + if (n != ZDFILE) + debug(F101,"zopeno fp[n]=stdout","",fp[n]); +#endif /* DEBUG */ + 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); + } + debug(F110,"zopeno fopen arg",p,0); + 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); +#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 long 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 %ld %s %c %s %c %c %s %s %d %s\n", + ctime(&timenow), /* date/time */ + gtimer(), /* elapsed secs */ + s, /* peer name */ + 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. */ + +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) + return(write(fileno(fp[n]),s,(int)strlen(s))); + 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 */ + +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)); + 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. */ + +int +zsoutx(n,s,x) int n, x; char *s; { +#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 */ + +#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 */ + +long +zgetfs(name) char *name; { + struct stat buf; + char fnam[CKMAXPATH+4]; + long size = -1L; + 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. +*/ +long +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; + 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 */ + 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); + + 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); + +#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); + 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. +*/ +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",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. +*/ + if (!diractive && haveonedir) { +#ifdef COMMENT + fcount = (mtchs == NULL && + (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) + ? 0 : 1; +#else + fcount = 0; + if (!mtchs) { + mtchs = (char **)malloc(maxnames * sizeof(*mtchs)); + if (mtchs) + fcount = 1; + if (!fcount) + return(nxpand = fcount); + } +#endif /* COMMENT */ + 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(F110,"zxpand haveonedir A2",*mtchptr,0); + 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, long n) +#else +zchkspa(f,n) char *f; long 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; { + long k; int x; + struct stat buf; + + k = iflen % 1024L; /* File length in K */ + if (k != 0L) k = 1L; + xx->lengthk = (iflen / 1024L) + 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 + tmx += timezone; +#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); + + debug(F110,"zstime",f,0); + debug(F111,"zstime date",yy->date.val,yy->date.len); + +#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 = iswild(pl -> npart); + + 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)) +#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) ); + + if (ruid != -1) + return(realname); + + ruid = real_uid(); /* get our uid */ + + /* how about $USER or $LOGNAME? */ + if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */ + ckstrncpy(envname, c, 255); + if ((p = getpwnam(envname)) != NULL) { + if (p->pw_uid == ruid) { /* get passwd entry for envname */ + ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */ + return(realname); + } + } + } + + /* can we use loginname() ? */ + + if ((c = getlogin()) != NULL) { /* name from utmp file */ + ckstrncpy (loginname, c, UIDBUFLEN); + 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 */ + realname[0] = '\0'; /* no user name */ + ruid = -1; + return(NULL); + } + ckstrncpy(realname, p->pw_name, UIDBUFLEN); + 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 */ + return(dirname); + if (!strcmp(olddir,dirname)) { /* Same as last time */ + return(oldrealdir); /* so return old answer. */ + } else { + 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 */ + if (i == 1) { /* if just a "~" */ +#ifdef IKSD + if (inserver) + user = getpwnam(uidbuf); /* Get info on current user */ + else +#endif /* IKSD */ + { + char * p = whoami(); + if (p) + user = getpwnam(p); + else + user = NULL; + } + } else { + user = getpwnam(&temp[1]); /* otherwise on the specified user */ + } + } + 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); + 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); + if (shpath == NULL) { + 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",shpath,0); + } +#endif /* COMMENT */ +#endif /* aegis */ + shptr = shname = shpath; + while (*shptr != '\0') + if (*shptr++ == DIRSEP) + shname = shptr; + restorsigs(); /* Restore ignored signals */ + debug(F110,"zshcmd shname",shname,0); + if (s == NULL || *s == '\0') { /* Interactive shell requested? */ + execl(shpath,shname,"-i",NULL); /* Yes, do that */ + } else { /* Otherwise, */ + execl(shpath,shname,"-c",s,NULL); /* exec the given command */ + } /* If execl() failed, */ + 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... */ + +#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 if 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) + 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 +isdir(s) char *s; { + int x, needrlink = 0, islink = 0; + struct stat statbuf; + char fnam[CKMAXPATH+4]; + + if (!s) return(0); + 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: + Long int, 0-based, indicating desired position. + Returns: + 0 on success. + -1 on failure. +*/ +#ifndef NORESEND +int +#ifdef CK_ANSIC +zfseek(long pos) +#else +zfseek(pos) long pos; +#endif /* CK_ANSIC */ +/* zfseek */ { + zincnt = -1; /* Must empty the input buffer */ + debug(F101,"zfseek","",pos); + return(fseek(fp[ZIFILE], pos, 0)?-1:0); +} +#endif /* NORESEND */ + +/* Z F N Q F P -- Convert filename to fully qualified absolute pathname */ + +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); + debug(F111,"sgetpwnam","getspnam()",sp); + if (sp == NULL) + 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 + char *getusershell(); +#endif /* GETUSERSHELL */ + +#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 (strcmp(cp, shell) == 0) + break; + } + debug(F100,"zvuser endusershell 1","",0); + endusershell(); + 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 */ + +int +zvpass(p) char *p; { + 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); + if (*p) + pam_pw = p; + 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 */ diff --git a/ckuins.txt b/ckuins.txt new file mode 100644 index 0000000..86448a3 --- /dev/null +++ b/ckuins.txt @@ -0,0 +1,3519 @@ + +C-Kermit 8.0 Unix Installation Instructions + + [ [1]Contents ] [ [2]C-Kermit ] [ [3]Kermit Home ] + + Frank da Cruz + The Kermit Project + Columbia University + + As of C-Kermit version: 8.0.211, 10 April 2004 + This file last updated: Tue Apr 13 10:14:33 2004 (New York City + 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: + +[4]http://www.columbia.edu/kermit/ckuins.html + __________________________________________________________________________ + +CONTENTS + + [5]OVERVIEW + + 1. [6]INTERNET QUICK START + 2. [7]INSTALLING FROM PACKAGES + 3. [8]INSTALLING PREBUILT BINARIES + 4. [9]BUILDING FROM SOURCE CODE + 5. [10]INSTALLING THE KERMIT FILES + 6. [11]INSTALLING UNIX C-KERMIT FROM DOS-FORMAT DISKETTES + 7. [12]CHECKING THE RESULTS + 8. [13]REDUCING THE SIZE OF THE EXECUTABLE PROGRAM IMAGE + 9. [14]UNIX VERSIONS + 10. [15]DIALING OUT AND COORDINATING WITH UUCP + 11. [16]RUNNING UNIX C-KERMIT SETUID OR SETGID + 12. [17]CONFIGURING UNIX WORKSTATIONS + 13. [18]BIZARRE BEHAVIOR AT RUNTIME + 14. [19]CRASHES AND CORE DUMPS + 15. [20]SYSLOGGING + 16. [21]BUILDING SECURE VERSIONS OF C-KERMIT 8.0 + 17. [22]INSTALLING C-KERMIT AS AN SSH SERVER SUBSYSTEM + __________________________________________________________________________ + +OVERVIEW + + [ [23]Top ] [ [24]Contents ] [ [25]Next ] + + WARNING: This document contains notes that have been accumulating + since the early 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 [26]Configurations + Options document. For troubleshooting after installation, see the + [27]General Hints and Tips and [28]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 [29]C-Kermit Program Logic Manual + + You may install C-Kermit: + + * From an "[30]install package", if one is available. + * As a [31]prebuilt binary, if available, plus accompanying text + files. + * By building from [32]source code. + __________________________________________________________________________ + +1. INTERNET QUICK START + + [ [33]Top ] [ [34]Contents ] [ [35]Next ] [ [36]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: + [37]ftp://kermit.columbia.edu/kermit/archives/cku211.tar.Z + (compress format) or + [38]ftp://kermit.columbia.edu/kermit/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: + [39]ftp://kermit.columbia.edu/kermit/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 [40]try it out to make sure it + works. Then read [41]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 [42]Sections 5 and [43]16. + __________________________________________________________________________ + +2. INSTALLING FROM PACKAGES + + [ [44]Top ] [ [45]Contents ] [ [46]Next ] [ [47]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 [48]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 [49]Sections 10 and [50]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 [51]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." + 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 ([52]next section) from other sites are + likely to have library mismatches. [53]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 [54]Section + 10 for details. + __________________________________________________________________________ + +3. INSTALLING PREBUILT BINARIES + + [ [55]Top ] [ [56]Contents ] [ [57]Next ] [ [58]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 [59]kermit/bin area (with names starting + with "ck"), also accessible on the [60]C-Kermit website. To install a + prebuilt binary: + + a. Rename the binary to "wermit". + b. Make sure it works; some tests are suggested in [61]Section 7. + c. Follow steps (b) through (e) in [62]Section 4. + d. Install related files as described in [63]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 [64]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 usually safe to run + a binary built on (or for) an older OS release on a newer one. + + Therefore, it is better to build your own binary from source code + ([65]next section) if you can. But since it is increasingly for Unix + systems (not to mention VMS and other OS's) to be delivered without C + compilers, it is often impractical. In such cases, try the most + appropriate prebuilt binary or binaries, and if none of them work, + [66]contact us and we'll see what we can do to help. + __________________________________________________________________________ + +4. BUILDING FROM SOURCE CODE + + [ [67]Top ] [ [68]Contents ] [ [69]Next ] [ [70]Previous ] + + Also see: [71]Section 8 and [72]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, and the linker. It + does not use any 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 [73]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 [74]Plan 9 and [75]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 ([76]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-ransfer 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 [77]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: + +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. + + 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 [78]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 [79]Section 7 for + suggestions). If it's OK, proceed; otherwise [80]notify us. + + NOTE: steps (c) through (e) can be accomplished using the + [81]makefile 'install' target as described in [82]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 + [83]Sections 10 and [84]11 for details). + d. Install the man page, ckuker.nr, with your other man pages. + e. Install the accompanying text files (see [85]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 [86]IKSD Administrator's Guide. + ________________________________________________________________________ + + 4.0. Special Considerations for C-Kermit 8.0 + + [ [87]Top ] [ [88]Contents ] [ [89]Next ] + + Also see: [90]C-Kermit Configuration Options + + SECTION CONTENTS + +4.1. [91]The Unix Makefile +4.2. [92]The C-Kermit Initialization File +4.3. [93]The 2.x BSD Makefile +4.4. [94]The Plan 9 Makefile +4.5. [95]Makefile Failures + + (Also see the [96]Configurations Options document, [97]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 [98]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 [99]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 [100]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 Plugable 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 + [101]ckufio.c, and [102]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 [103]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 [104]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, [105]ckutio.c or [106]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 [107]ckutio.c or [108]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 [109]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 [110]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 [111]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 [112]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: + +[113]kermit-support@columbia.edu + ________________________________________________________________________ + + 4.1. The Unix Makefile + + [ [114]Top ] [ [115]Contents ] [ [116]Section Contents ] [ [117]Next ] + [ [118]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 [119]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 [120]ckubwr.txt file ([121]CLICK HERE for the Web version). + + If you have trouble with building [122]ckwart.c, or running the + resulting wart preprocessor program on [123]ckcpro.w: + + 1. Just "touch" the [124]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 + + [ [125]Top ] [ [126]Contents ] [ [127]Section Contents ] [ [128]Next ] + [ [129]Previous ] + + (This section is obsolete.) Read [130]Section 5 about the + initialization file. + ________________________________________________________________________ + + 4.3. The 2.x BSD Makefile + + [ [131]Top ] [ [132]Contents ] [ [133]Section Contents ] [ [134]Next ] + [ [135]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 + + [ [136]Top ] [ [137]Contents ] [ [138]Section Contents ] [ [139]Next ] + [ [140]Previous ] + + Use the separate makefile [141]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 + + [ [142]Top ] [ [143]Contents ] [ [144]Section Contents ] [ + [145]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 [146]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 [147]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 [148]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 + + [ [149]Top ] [ [150]Contents ] [ [151]Next ] [ [152]Previous ] + + SECTION CONTENTS + +5.1. [153]The C-Kermit Initialization File +5.2. [154]Text Files +5.3. [155]Installing the Kermit Files +5.4. [156]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: + +[157]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 [158]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. + + 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 [159]next section. + + [160]COPYING.TXT + Copyright notice, permissions, and disclaimer. + + [161]ckermit.ini + The standard initialization file, intended more for reference + (in most cases) than actual use; see [162]Section 5.1. + + [163]ckermod.ini + A sample customization file. + + [164]ckermit70.txt + Supplement to [165]Using C-Kermit for version 7.0. Available on + the Kermit website as: + [166]http://www.columbia.edu/kermit/ckermit70.html + + [167]ckermit80.txt + Supplement to [168]Using C-Kermit for version 8.0. Available on + the Kermit website as: + [169]http://www.columbia.edu/kermit/ckermit80.html + + [170]ckcbwr.txt + The general C-Kermit hints and tips ("beware") file. Available + on the Kermit website as: + [171]http://www.columbia.edu/kermit/ckcbwr.html + + [172]ckubwr.txt + The Unix-specific C-Kermit hints and tips file. Available on + the Kermit website as: + [173]http://www.columbia.edu/kermit/ckubwr.html + + [174]ckuins.txt + Unix C-Kermit Installation Instructions (this file). Available + on the Kermit website as: + [175]http://www.columbia.edu/kermit/ckuins.html + + [176]ckccfg.txt + C-Kermit compile-time configuration options. Available on the + Kermit website as: + [177]http://www.columbia.edu/kermit/ckccfg.html + + [178]ckcplm.txt + The C-Kermit program logic manual. Available on the Kermit + website as: + [179]http://www.columbia.edu/kermit/ckcplm.html + + [180]ca_certs.pem + Certificate Authority certificates for secure connections (see + [181]Section 16). + + 5.3. Installing the Kermit Files + + There is an "install" target in the [182]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 + [183]Sections 10 and [184]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 [185]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 [186]complete C-Kermit archive and built + C-Kermit from source; or: + * You downloaded an [187]individual C-Kermit binary and the + [188]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 + + [ [189]Top ] [ [190]Contents ] [ [191]Next ] [ [192]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 + + [ [193]Top ] [ [194]Contents ] [ [195]Next ] [ [196]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 [197]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 [198]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 [199]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 + [200]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 [201]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 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 [202]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 [203]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 [204]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 [205]Section 2) of the + [206]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 [207]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 [208]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 [209]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 + [210]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 [211]ckutio.c and see if you can add the + needed code (see the section on [212]HARDWARE FLOW CONTROL below). + (And please [213]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 + + [ [214]Top ] [ [215]Contents ] [ [216]Next ] [ [217]Previous ] + + Also see: [218]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 [219]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 + + [ [220]Top ] [ [221]Contents ] [ [222]Next ] [ [223]Previous ] + + SECTION CONTENTS + +9.1 [224]Standards + 9.1.1. [225]POSIX + 9.1.2. [226]ANSI C + 9.1.3. [227]Other Standards +9.2. [228]Library Issues +9.3. [229]Unix File System Peculiarities +9.4. [230]Hardware Flow Control +9.5. [231]Terminal Speeds +9.6. [232]Millisecond Sleeps +9.7. [233]Nondestructive Input Buffer Peeking +9.8. [234]Other System-Dependent Features +9.9. [235]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 [236]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 + + [ [237]Top ] [ [238]Section Contents ] [ [239]Contents ] [ [240]Next ] + + SUBSECTION CONTENTS + +9.1.1. [241]POSIX +9.1.2. [242]ANSI C +9.1.3. [243]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 dilimma 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 + + [ [244]Top ] [ [245]Contents ] [ [246]Section Contents ] [ + [247]Subsection Contents ] [ [248]Next ] [ [249]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/bureacratic 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 + + [ [250]Top ] [ [251]Contents ] [ [252]Section Contents ] [ + [253]Subsection Contents ] [ [254]Next ] [ [255]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, [256]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 + [257]9.1.1). + ________________________________________________________________________ + + 9.2. Library Issues + + [ [258]Top ] [ [259]Contents ] [ [260]Section Contents ] [ + [261]Subsection Contents ] [ [262]Next ] [ [263]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 + + [ [264]Top ] [ [265]Contents ] [ [266]Section Contents ] [ [267]Next ] + [ [268]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 [269]ckufio.c and + [270]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 [271]ckufio.c. + ________________________________________________________________________ + + 9.4. Hardware Flow Control + + [ [272]Top ] [ [273]Contents ] [ [274]Section Contents ] [ [275]Next ] + [ [276]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 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 [277]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 + [278]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 + + [ [279]Top ] [ [280]Contents ] [ [281]Section Contents ] [ [282]Next ] + [ [283]Previous ] + + The allowable speeds for the SET SPEED command are defined in + [284]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 [285]ckuus3.c, and the corresponding case to + ttsspd() in [286]ckutio.c. + ________________________________________________________________________ + + 9.6. Millisecond Sleeps + + [ [287]Top ] [ [288]Contents ] [ [289]Section Contents ] [ [290]Next ] + [ [291]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 + + [ [292]Top ] [ [293]Contents ] [ [294]Section Contents ] [ [295]Next ] + [ [296]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 + + [ [297]Top ] [ [298]Contents ] [ [299]Section Contents ] [ [300]Next ] + [ [301]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 [302]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 [303]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 + [304]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 [305]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 + + [ [306]Top ] [ [307]Contents ] [ [308]Section Contents ] [ [309]Next ] + [ [310]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 + + [ [311]Top ] [ [312]Contents ] [ [313]Next ] [ [314]Previous ] + + 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 this section and the next. + + The short version: + + 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 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, etc. + + Rather than change Unix to enforce exclusive access to serial devices + such as ttys, Unix developers chose instead to use a "lock file". 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 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 [315]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 simulataneously 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, which is becoming increasingly popular, 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 + gurantee 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 appropropriate 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 [316]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 + + [ [317]Top ] [ [318]Contents ] [ [319]Next ] [ [320]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 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 + [321]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 [322]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 + + [ [323]Top ] [ [324]Contents ] [ [325]Next ] [ [326]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 + + [ [327]Top ] [ [328]Contents ] [ [329]Next ] [ [330]Previous ] + + See the "beware file", + + [331]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 [332]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 + + [ [333]Top ] [ [334]Contents ] [ [335]Next ] [ [336]Previous ] + + If C-Kermit constitently 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 + + [ [337]Top ] [ [338]Contents ] [ [339]Next ] [ [340]Previous ] + + "Syslogging" means recording selected 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 [341]Section 4.2 of the [342]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, + e.g.: + +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. + + Level 2 and 3 records are equivalent to those in the connection log; + see the [343]C-Kermit 7.0 Supplement) for a detailed description of + the connection log. + __________________________________________________________________________ + +16. BUILDING SECURE VERSIONS OF C-KERMIT 8.0 + + [ [344]Top ] [ [345]Contents ] [ [346]Next ] [ [347]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 [348]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 + [349]Kermit Security Reference. + + C-Kermit 8.0 comes with a current list of Certificate Authority + certificates, including one for the Kermit Project that can be used + for authentication to Columbia's [350]Internet Kermit Service (IKSD). + You can use C-Kermit 7.0 or later to access Columbia's IKSD securely + by installing the Kermit Project certificate in + /usr/local/ssl/cert.pem (or the appropriate location based upon the + installation of OpenSSL on your system). You can find a copy of the + certificates file at: + +[351]ftp://kermit.columbia.edu/kermit/c-kermit/ca_certs.pem + __________________________________________________________________________ + +17. INSTALLING C-KERMIT AS AN SSH SERVER SUBSYSTEM + + [ [352]Top ] [ [353]Contents ] [ [354]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: + +[355]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 susbsystem. + 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. + + [ [356]Top ] [ [357]Contents ] [ [358]C-Kermit Home ] [ [359]C-Kermit + 8.0 Overview ] [ [360]Kermit Home ] + _________________________________________________________________ + + + C-Kermit 8.0 Unix Installation Instructions / The Kermit Project / + Columbia University / 10 April 2004 + +References + + 1. http://www.columbia.edu/kermit/ckuins.html#contents + 2. http://www.columbia.edu/kermit/ckermit.html + 3. http://www.columbia.edu/kermit/index.html + 4. http://www.columbia.edu/kermit/ckuins.html + 5. http://www.columbia.edu/kermit/ckuins.html#x0 + 6. http://www.columbia.edu/kermit/ckuins.html#x1 + 7. http://www.columbia.edu/kermit/ckuins.html#x2 + 8. http://www.columbia.edu/kermit/ckuins.html#x3 + 9. http://www.columbia.edu/kermit/ckuins.html#x4 + 10. http://www.columbia.edu/kermit/ckuins.html#x5 + 11. http://www.columbia.edu/kermit/ckuins.html#x6 + 12. http://www.columbia.edu/kermit/ckuins.html#x7 + 13. http://www.columbia.edu/kermit/ckuins.html#x8 + 14. http://www.columbia.edu/kermit/ckuins.html#x9 + 15. http://www.columbia.edu/kermit/ckuins.html#x10 + 16. http://www.columbia.edu/kermit/ckuins.html#x11 + 17. http://www.columbia.edu/kermit/ckuins.html#x12 + 18. http://www.columbia.edu/kermit/ckuins.html#x13 + 19. http://www.columbia.edu/kermit/ckuins.html#x14 + 20. http://www.columbia.edu/kermit/ckuins.html#x15 + 21. http://www.columbia.edu/kermit/ckuins.html#x16 + 22. http://www.columbia.edu/kermit/ckuins.html#x16 + 23. http://www.columbia.edu/kermit/ckuins.html#top + 24. http://www.columbia.edu/kermit/ckuins.html#contents + 25. http://www.columbia.edu/kermit/ckuins.html#x1 + 26. http://www.columbia.edu/kermit/ckccfg.html + 27. http://www.columbia.edu/kermit/ckcbwr.html + 28. http://www.columbia.edu/kermit/ckubwr.html + 29. http://www.columbia.edu/kermit/ckcplm.html + 30. http://www.columbia.edu/kermit/ckuins.html#x2 + 31. http://www.columbia.edu/kermit/x3 + 32. http://www.columbia.edu/kermit/ckuins.html#x4 + 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#x2 + 36. http://www.columbia.edu/kermit/ckuins.html#x0 + 37. ftp://kermit.columbia.edu/kermit/archives/cku211.tar.Z + 38. ftp://kermit.columbia.edu/kermit/archives/cku211.tar.gz + 39. ftp://kermit.columbia.edu/kermit/archives/cku211.tar + 40. http://www.columbia.edu/kermit/ckuins.html#x7 + 41. http://www.columbia.edu/kermit/ckuins.html#x5 + 42. http://www.columbia.edu/kermit/ckuins.html#x5 + 43. http://www.columbia.edu/kermit/ckuins.html#x16 + 44. http://www.columbia.edu/kermit/ckuins.html#top + 45. http://www.columbia.edu/kermit/ckuins.html#contents + 46. http://www.columbia.edu/kermit/ckuins.html#x3 + 47. http://www.columbia.edu/kermit/ckuins.html#x1 + 48. http://www.columbia.edu/kermit/ckuins.html#x5 + 49. http://www.columbia.edu/kermit/ckuins.html#X10 + 50. http://www.columbia.edu/kermit/ckuins.html#x11 + 51. http://www.columbia.edu/kermit/ckuins.html#x10 + 52. http://www.columbia.edu/kermit/ckuins.html#x3 + 53. http://www.columbia.edu/kermit/ck80packages.html + 54. http://www.columbia.edu/kermit/ckuins.html#x10 + 55. http://www.columbia.edu/kermit/ckuins.html#top + 56. http://www.columbia.edu/kermit/ckuins.html#contents + 57. http://www.columbia.edu/kermit/ckuins.html#x4 + 58. http://www.columbia.edu/kermit/ckuins.html#x2 + 59. ftp://kermit.columbia.edu/kermit/bin/ + 60. http://www.columbia.edu/kermit/ck80binaries.html + 61. http://www.columbia.edu/kermit/ckuins.html#x7 + 62. http://www.columbia.edu/kermit/ckuins.html#build + 63. http://www.columbia.edu/kermit/ckuins.html#x5 + 64. http://www.columbia.edu/kermit/ckuins.html#x4 + 65. http://www.columbia.edu/kermit/ckuins.html#x4 + 66. mailto:kermit@columbia.edu + 67. http://www.columbia.edu/kermit/ckuins.html#top + 68. http://www.columbia.edu/kermit/ckuins.html#contents + 69. http://www.columbia.edu/kermit/ckuins.html#x5 + 70. http://www.columbia.edu/kermit/ckuins.html#x3 + 71. http://www.columbia.edu/kermit/ckuins.html#x8 + 72. http://www.columbia.edu/kermit/ckuins.html#x9 + 73. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 74. ftp://kermit.columbia.edu/kermit/c-kermit/ckpker.mk + 75. ftp://kermit.columbia.edu/kermit/c-kermit/ckubsd.mak + 76. http://www.columbia.edu/kermit/ckuins.html#x5 + 77. mailto:kermit-support@columbia.edu + 78. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 79. http://www.columbia.edu/kermit/ckuins.html#x7 + 80. mailto:kermit-support@columbia.edu + 81. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 82. http://www.columbia.edu/kermit/ckuins.html#x5.4 + 83. http://www.columbia.edu/kermit/ckuins.html#x10 + 84. http://www.columbia.edu/kermit/ckuins.html#x11 + 85. http://www.columbia.edu/kermit/ckuins.html#x5 + 86. http://www.columbia.edu/kermit/iksd.html + 87. http://www.columbia.edu/kermit/ckuins.html#top + 88. http://www.columbia.edu/kermit/ckuins.html#contents + 89. http://www.columbia.edu/kermit/ckuins.html#x4.1 + 90. http://www.columbia.edu/kermit/ckccfg.html + 91. http://www.columbia.edu/kermit/ckuins.html#x4.1 + 92. http://www.columbia.edu/kermit/ckuins.html#x4.2 + 93. http://www.columbia.edu/kermit/ckuins.html#x4.3 + 94. http://www.columbia.edu/kermit/ckuins.html#x4.4 + 95. http://www.columbia.edu/kermit/ckuins.html#x4.5 + 96. http://www.columbia.edu/kermit/ckccfg.html + 97. http://www.columbia.edu/kermit/ckccfg.html#x8 + 98. http://www.columbia.edu/kermit/iksd.html + 99. http://www.columbia.edu/kermit/iksd.html + 100. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 101. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 102. mailto:kermit-support@columbia.edu + 103. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c + 104. http://www.columbia.edu/kermit/ckuins.html#x15 + 105. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 106. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 109. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c + 110. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c + 111. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 112. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.c + 113. mailto:kermit-support@columbia.edu + 114. http://www.columbia.edu/kermit/ckuins.html#top + 115. http://www.columbia.edu/kermit/ckuins.html#contents + 116. http://www.columbia.edu/kermit/ckuins.html#x4 + 117. http://www.columbia.edu/kermit/ckuins.html#x4.2 + 118. http://www.columbia.edu/kermit/ckuins.html#x4.0 + 119. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 120. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt + 121. http://www.columbia.edu/kermit/ckubwr.html + 122. ftp://kermit.columbia.edu/kermit/c-kermit/ckwart.c + 123. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w + 124. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.c + 125. http://www.columbia.edu/kermit/ckuins.html#top + 126. http://www.columbia.edu/kermit/ckuins.html#contents + 127. http://www.columbia.edu/kermit/ckuins.html#x4 + 128. http://www.columbia.edu/kermit/ckuins.html#x4.3 + 129. http://www.columbia.edu/kermit/ckuins.html#x4.1 + 130. http://www.columbia.edu/kermit/ckuins.html#x5 + 131. http://www.columbia.edu/kermit/ckuins.html#top + 132. http://www.columbia.edu/kermit/ckuins.html#contents + 133. http://www.columbia.edu/kermit/ckuins.html#x4 + 134. http://www.columbia.edu/kermit/ckuins.html#x4.4 + 135. http://www.columbia.edu/kermit/ckuins.html#x4.2 + 136. http://www.columbia.edu/kermit/ckuins.html#top + 137. http://www.columbia.edu/kermit/ckuins.html#contents + 138. http://www.columbia.edu/kermit/ckuins.html#x4 + 139. http://www.columbia.edu/kermit/ckuins.html#x4.5 + 140. http://www.columbia.edu/kermit/ckuins.html#x4.3 + 141. ftp://kermit.columbia.edu/kermit/c-kermit/ckpker.mk + 142. http://www.columbia.edu/kermit/ckuins.html#top + 143. http://www.columbia.edu/kermit/ckuins.html#contents + 144. http://www.columbia.edu/kermit/ckuins.html#x4 + 145. http://www.columbia.edu/kermit/ckuins.html#x4.4 + 146. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 147. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 148. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w + 149. http://www.columbia.edu/kermit/ckuins.html#top + 150. http://www.columbia.edu/kermit/ckuins.html#contents + 151. http://www.columbia.edu/kermit/ckuins.html#x6 + 152. http://www.columbia.edu/kermit/ckuins.html#x4 + 153. http://www.columbia.edu/kermit/ckuins.html#x5.1 + 154. http://www.columbia.edu/kermit/ckuins.html#x5.2 + 155. http://www.columbia.edu/kermit/ckuins.html#x5.3 + 156. http://www.columbia.edu/kermit/ckuins.html#x5.4 + 157. http://www.columbia.edu/kermit/ + 158. http://www.columbia.edu/kermit/ckuins.html#x5.4 + 159. http://www.columbia.edu/kermit/ckuins.html#x5.3 + 160. ftp://kermit.columbia.edu/kermit/c-kermit/COPYING.TXT + 161. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit.ini + 162. http://www.columbia.edu/kermit/ckuins.html#x5.1 + 163. ftp://kermit.columbia.edu/kermit/c-kermit/ckermod.ini + 164. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit70.txt + 165. http://www.columbia.edu/kermit/ck60manual + 166. http://www.columbia.edu/kermit/ckermit70.html + 167. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit80.txt + 168. http://www.columbia.edu/kermit/ck60manual + 169. http://www.columbia.edu/kermit/ckermit80.html + 170. ftp://kermit.columbia.edu/kermit/c-kermit/ckcbwr.txt + 171. http://www.columbia.edu/kermit/ckcbwr.html + 172. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt + 173. http://www.columbia.edu/kermit/ckubwr.html + 174. ftp://kermit.columbia.edu/kermit/c-kermit/ckuins.txt + 175. http://www.columbia.edu/kermit/ckuins.html + 176. ftp://kermit.columbia.edu/kermit/c-kermit/ckccfg.txt + 177. http://www.columbia.edu/kermit/ckccfg.html + 178. ftp://kermit.columbia.edu/kermit/c-kermit/ckcplm.txt + 179. http://www.columbia.edu/kermit/ckcplm.html + 180. ftp://kermit.columbia.edu/kermit/c-kermit/ca_certs.pem + 181. http://www.columbia.edu/kermit/ckuins.html#x16" + 182. ftp://kermit.columbia.edu/kermit/c-kermit/makefile + 183. http://www.columbia.edu/kermit/ckuins.html#x? + 184. http://www.columbia.edu/kermit/ckuins.html#x11 + 185. http://www.columbia.edu/kermit/ckuins.html#x5.2 + 186. http://www.columbia.edu/kermit/ckermit.html#download + 187. http://www.columbia.edu/kermit/ck80binaries.html + 188. http://www.columbia.edu/kermit/ckermit.html#download + 189. http://www.columbia.edu/kermit/ckuins.html#top + 190. http://www.columbia.edu/kermit/ckuins.html#contents + 191. http://www.columbia.edu/kermit/ckuins.html#x7 + 192. http://www.columbia.edu/kermit/ckuins.html#x5 + 193. http://www.columbia.edu/kermit/ckuins.html#top + 194. http://www.columbia.edu/kermit/ckuins.html#contents + 195. http://www.columbia.edu/kermit/ckuins.html#x8 + 196. http://www.columbia.edu/kermit/ckuins.html#x6 + 197. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 198. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 199. http://www.columbia.edu/kermit/ckuins.html#x4.0 + 200. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 201. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 202. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 203. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 204. http://www.columbia.edu/kermit/ckuins.html#x10 + 205. http://www.columbia.edu/kermit/ckccfg.html#x2 + 206. http://www.columbia.edu/kermit/ckccfg.html + 207. http://www.columbia.edu/kermit/ckuins.html#x4 + 208. http://www.columbia.edu/kermit/ckuins.html#x10 + 209. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 210. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 211. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 212. http://www.columbia.edu/kermit/ckuins.html#x9.4 + 213. mailto:kermit-support@columbia.edu + 214. http://www.columbia.edu/kermit/ckuins.html#top + 215. http://www.columbia.edu/kermit/ckuins.html#contents + 216. http://www.columbia.edu/kermit/ckuins.html#x9 + 217. http://www.columbia.edu/kermit/ckuins.html#x7 + 218. http://www.columbia.edu/kermit/ckccfg.html + 219. http://www.columbia.edu/kermit/ckccfg.html + 220. http://www.columbia.edu/kermit/ckuins.html#top + 221. http://www.columbia.edu/kermit/ckuins.html#contents + 222. http://www.columbia.edu/kermit/ckuins.html#x10 + 223. http://www.columbia.edu/kermit/ckuins.html#x8 + 224. http://www.columbia.edu/kermit/ckuins.html#x9.1 + 225. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 + 226. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 + 227. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 + 228. http://www.columbia.edu/kermit/ckuins.html#x9.2 + 229. http://www.columbia.edu/kermit/ckuins.html#x9.3 + 230. http://www.columbia.edu/kermit/ckuins.html#x9.4 + 231. http://www.columbia.edu/kermit/ckuins.html#x9.5 + 232. http://www.columbia.edu/kermit/ckuins.html#x9.6 + 233. http://www.columbia.edu/kermit/ckuins.html#x9.7 + 234. http://www.columbia.edu/kermit/ckuins.html#x9.8 + 235. http://www.columbia.edu/kermit/ckuins.html#x9.9 + 236. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 237. http://www.columbia.edu/kermit/ckuins.html#top + 238. http://www.columbia.edu/kermit/ckuins.html#x9 + 239. http://www.columbia.edu/kermit/ckuins.html#contents + 240. http://www.columbia.edu/kermit/ckuins.html#x9.2 + 241. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 + 242. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 + 243. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 + 244. http://www.columbia.edu/kermit/ckuins.html#top + 245. http://www.columbia.edu/kermit/ckuins.html#contents + 246. http://www.columbia.edu/kermit/ckuins.html#x9 + 247. http://www.columbia.edu/kermit/ckuins.html#x9.1 + 248. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 + 249. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 + 250. http://www.columbia.edu/kermit/ckuins.html#top + 251. http://www.columbia.edu/kermit/ckuins.html#contents + 252. http://www.columbia.edu/kermit/ckuins.html#x9 + 253. http://www.columbia.edu/kermit/ckuins.html#x9.1 + 254. http://www.columbia.edu/kermit/ckuins.html#x9.2 + 255. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 + 256. http://www.opengroup.org/onlinepubs/007904975/ + 257. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 + 258. http://www.columbia.edu/kermit/ckuins.html#top + 259. http://www.columbia.edu/kermit/ckuins.html#contents + 260. http://www.columbia.edu/kermit/ckuins.html#x9 + 261. http://www.columbia.edu/kermit/ckuins.html#x9.1 + 262. http://www.columbia.edu/kermit/ckuins.html#x9.3 + 263. http://www.columbia.edu/kermit/ckuins.html#x9.1 + 264. http://www.columbia.edu/kermit/ckuins.html#top + 265. http://www.columbia.edu/kermit/ckuins.html#contents + 266. http://www.columbia.edu/kermit/ckuins.html#x9 + 267. http://www.columbia.edu/kermit/ckuins.html#x9.4 + 268. http://www.columbia.edu/kermit/ckuins.html#x9.2 + 269. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 270. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 271. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 272. http://www.columbia.edu/kermit/ckuins.html#top + 273. http://www.columbia.edu/kermit/ckuins.html#contents + 274. http://www.columbia.edu/kermit/ckuins.html#x9 + 275. http://www.columbia.edu/kermit/ckuins.html#x9.5 + 276. http://www.columbia.edu/kermit/ckuins.html#x9.3 + 277. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 278. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 279. http://www.columbia.edu/kermit/ckuins.html#top + 280. http://www.columbia.edu/kermit/ckuins.html#contents + 281. http://www.columbia.edu/kermit/ckuins.html#x9 + 282. http://www.columbia.edu/kermit/ckuins.html#x9.6 + 283. http://www.columbia.edu/kermit/ckuins.html#x9.4 + 284. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 285. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c + 286. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 287. http://www.columbia.edu/kermit/ckuins.html#top + 288. http://www.columbia.edu/kermit/ckuins.html#contents + 289. http://www.columbia.edu/kermit/ckuins.html#x9 + 290. http://www.columbia.edu/kermit/ckuins.html#x9.7 + 291. http://www.columbia.edu/kermit/ckuins.html#x9.5 + 292. http://www.columbia.edu/kermit/ckuins.html#top + 293. http://www.columbia.edu/kermit/ckuins.html#contents + 294. http://www.columbia.edu/kermit/ckuins.html#x9 + 295. http://www.columbia.edu/kermit/ckuins.html#x9.8 + 296. http://www.columbia.edu/kermit/ckuins.html#x9.6 + 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.9 + 301. http://www.columbia.edu/kermit/ckuins.html#x9.7 + 302. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 303. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 304. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h + 305. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 306. http://www.columbia.edu/kermit/ckuins.html#top + 307. http://www.columbia.edu/kermit/ckuins.html#contents + 308. http://www.columbia.edu/kermit/ckuins.html#x9 + 309. http://www.columbia.edu/kermit/ckuins.html#x10 + 310. http://www.columbia.edu/kermit/ckuins.html#x9.8 + 311. http://www.columbia.edu/kermit/ckuins.html#top + 312. http://www.columbia.edu/kermit/ckuins.html#contents + 313. http://www.columbia.edu/kermit/ckuins.html#x11 + 314. http://www.columbia.edu/kermit/ckuins.html#x9 + 315. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 316. http://www.columbia.edu/kermit/ckuins.html#x11 + 317. http://www.columbia.edu/kermit/ckuins.html#top + 318. http://www.columbia.edu/kermit/ckuins.html#contents + 319. http://www.columbia.edu/kermit/ckuins.html#x12 + 320. http://www.columbia.edu/kermit/ckuins.html#x10 + 321. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 322. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c + 323. http://www.columbia.edu/kermit/ckuins.html#top + 324. http://www.columbia.edu/kermit/ckuins.html#contents + 325. http://www.columbia.edu/kermit/ckuins.html#x13 + 326. http://www.columbia.edu/kermit/ckuins.html#x11 + 327. http://www.columbia.edu/kermit/ckuins.html#top + 328. http://www.columbia.edu/kermit/ckuins.html#contents + 329. http://www.columbia.edu/kermit/ckuins.html#x14 + 330. http://www.columbia.edu/kermit/ckuins.html#x12 + 331. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt + 332. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c + 333. http://www.columbia.edu/kermit/ckuins.html#top + 334. http://www.columbia.edu/kermit/ckuins.html#contents + 335. http://www.columbia.edu/kermit/ckuins.html#x15 + 336. http://www.columbia.edu/kermit/ckuins.html#x13 + 337. http://www.columbia.edu/kermit/ckuins.html#top + 338. http://www.columbia.edu/kermit/ckuins.html#contents + 339. http://www.columbia.edu/kermit/ckuins.html#x16 + 340. http://www.columbia.edu/kermit/ckuins.html#x14 + 341. http://www.columbia.edu/kermit/iksd.html#x4.2 + 342. http://www.columbia.edu/kermit/iksd.html + 343. http://www.columbia.edu/kermit/ckermit2.html + 344. http://www.columbia.edu/kermit/ckuins.html#top + 345. http://www.columbia.edu/kermit/ckuins.html#contents + 346. http://www.columbia.edu/kermit/ckuins.html#x17 + 347. http://www.columbia.edu/kermit/ckuins.html#x15 + 348. http://www.columbia.edu/kermit/security.html + 349. http://www.columbia.edu/kermit/security80.html + 350. http://www.columbia.edu/kermit/cuiksd.html + 351. ftp://kermit.columbia.edu/kermit/c-kermit/ca_certs.pem + 352. http://www.columbia.edu/kermit/ckuins.html#top + 353. http://www.columbia.edu/kermit/ckuins.html#contents + 354. http://www.columbia.edu/kermit/ckuins.html#x16 + 355. http://www.columbia.edu/kermit/skermit.html + 356. http://www.columbia.edu/kermit/ckuins.html#top + 357. http://www.columbia.edu/kermit/ckuins.html#contents + 358. http://www.columbia.edu/kermit/ckermit.html + 359. http://www.columbia.edu/kermit/ck80.html + 360. http://www.columbia.edu/kermit/index.html diff --git a/ckuker.nr b/ckuker.nr new file mode 100644 index 0000000..b26801d --- /dev/null +++ b/ckuker.nr @@ -0,0 +1,1827 @@ +.\" @(#) kermit.1 8.0.211 2004/04/10 Columbia University +.TH KERMIT 1 "APRIL 2004" "User Manuals" +.na +.SH NAME +kermit \- +.B C-Kermit 8.0: +transport- and platform-independent +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-Kermit 8.0. Although it might be rather long +for a Unix manual page, it's still far shorter than the 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 version 8.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-Kermit is an all-purpose communications software package from the Kermit +Project at Columbia University that: +.PP +.nf +\(bu Is portable to many platforms, Unix and non-Unix 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-file file transfer. +.br +\(bu Is customizable in every aspect of its operation. +.fi +.PP +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 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 [RFC2839, RFC2840]. +.PP +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. +.PP +This manual page offers an overview of C-Kermit 8.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 DOCUMENTATION below). For further information +about C-Kermit, Kermit software for other platforms, and Kermit manuals, +visit the Kermit Project website: +.PP + http://www.columbia.edu/kermit/ +.PP +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. +.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-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 \e%1, \e%2, ... \e%9. The "+" argument is +like "=" but for use in "kerbang scripts" (explained below). A second +command-line 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-line URL (if any, and if so, execution stops here). +.TP + 5. +Command-line options (if any). +.TP + 6. +Interactive commands. +.PP +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. +.IP +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 lacks +C-Kermit a feature mentioned here, use its SHOW FEATURES command to +see what might have been excluded. +.PP +C-Kermit has three kinds of commands: regular single-letter command-line +options, extended-format command-line options, and interactive commands. +.PP +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 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.) +.PP +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: +.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-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). Action options are +tagged "ACTION". +.TP +-0 +(digit zero) 100% transparent Connect state for +"in-the-middle" operation: 8 bits, no parity, no +escape character, everything passes through. +.TP +-8 +(digit eight) Connection is 8-bit clean (this is the +default in C-Kermit 8.0). 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-address-or-hostname[:optional-TCP-port]. +NOTE: C-Kermit also has a separate FTP command-line +personality, with regular FTP-like command-line +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-mode 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-Kermit also has a +separate Telnet command-line personality, with regular Telnet-like +command-line 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-Kermit 7.0 and later. +.TP +-R +Remote-only (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-file text/binary switching. +Equivalent to SET TRANSFER MODE MANUAL. +.TP +-Y +Skip (don't execute) the initialization file. +.TP +-a arg +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. +.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-transfer +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-letter command-line 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-duplex 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-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: + +.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-to 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-level 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-Kermit prints its version number. +.PP +Plus several other IKSD-Only options described at: +.PP + http://www.columbia.edu/kermit/iksd.html +.PP +See the file-transfer section for examples of command-line invocation. +.SH COMMAND LANGUAGE +C-Kermit's interactive command language is the subject of a 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: +.PP +.nf + $ kermit + (/current/directory) C-Kermit> +.fi +.PP +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). +.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-Kermit 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-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: +.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-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 ?". \em(xxx) is a user +defined variable (strictly speaking, it is a macro used as a variable). +.SS 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. +.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-level help. +.TP +HELP command +Requests help about the given command. +.TP +INTRODUCTION +Requests a brief introduction to C-Kermit. +.TP +LICENSE +Displays the C-Kermit software copyright and license. +.TP +VERSION +Displays C-Kermit'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-and/or-time ] +Shows current date-time or interprets given date-time. +.TP +RUN [ extern-command [ parameters... ] +Runs the given external command. Synonym: !. +.TP +EXEC [ extern-command [ 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-in, 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-command +Runs the command on a pseudoterminal as if it were a connection. +.TP +PIPE external-command +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-level 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-state escape character. +.RE +.SS +Closing Connections +.RS +.TP +HANGUP +Hangs up the currently open +serial-port or network connection. +.TP +CLOSE +Closes the currently open +serial-port or network connection. +.TP +SET LINE (with no devicename) +Closes the currently open +serial-port or network connection. +.TP +SET HOST (with no hostname) +Closes the currently open serial-port 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-name ] +Sends the given file. Synonym: S. +.TP +SEND [ options ] filespec +Sends all files that match. +.TP +RESEND [ options ] filespec +Resumes an interupted SEND from the point of failure. +.TP +RECEIVE [ options ] [ as-name ] +Waits passively for files to arrive. Synonym: R. +.TP +LOG TRANSACTIONS [ filename ] +Keeps a record of file transfers. +.TP +FAST +Use fast file-transfer settings (default). +.TP +CAUTIOUS +Use cautious and less fast file-transfer settings. +.TP +ROBUST +Use ultra-conservative and slow file-transfer 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-transfer 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-name ] +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 interupted SEND from the point of failure. +.TP +GET [ options ] remote-filespec +Asks the server to send the given files. Synonym: G. +.TP +REGET [ options ] remote-filespec +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-mail (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-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. +.PP +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. +.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-transfer 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-level 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-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: +.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-transfer 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-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-\e C) and +type "e-packet" and then press the Return or Enter key; this forces a +fatal Kermit protocol error. +.TP +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. +.TP +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. +.sp +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. +.sp +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? 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-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. +.TP +Making a Telnet Connection +At the C-Kermit 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-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. +.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-through) 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-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. +.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-server 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-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. +.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-modem 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-connected + set speed 57600 ; Or other desired speed + set flow rts/cts ; If RTS and CTS are cross-connected + 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-bit 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-Kermit 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-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 C-Kermit (7.0 or later) and 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). +.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-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. +.PP +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. +.TP +Downloading Files +Let's say you have Kermit 95, C-Kermit, or 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: +.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-connecting, 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-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. +.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-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. +.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-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 . . . +.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-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. +.RE +.sp +Obviously the success and performance of a file transfer also depends +on C-Kermit's file transfer partner. Up-to-date, 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-Kermit, or +send email to kermit-support@columbia.edu. +.TP +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 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-file text/binary mode switching: +SET TRANSFER MODE { AUTOMATIC, MANUAL } (HELP SET TRANSFER). +.TP +Cross-platform 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-set 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-Kermit File Transfer +You can also use C-Kermit 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-IN 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-related 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-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: +.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-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: +.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-Kermit if +you already have an SSH client installed. +.SH ALTERNATIVE COMMAND-LINE PERSONALITIES +When invoked as "kermit" or any other name besides "ftp" or "telnet", +C-Kermit has the command-line options described above in the OPTIONS +section. However, if you invoke C-Kermit as "telnet" or "ftp", it changes +its command-line personality 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: +.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-line 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-personality command-line +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-line 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-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: +.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-Kermit 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-Kermit 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-Kermit or bundle it +with a product or otherwise distribute it in a commercial setting EXCEPT +WITH AN OPEN-SOURCE 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-Kermit 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-Kermit bugs, hints, tips. +.TP +ckubwr.txt +Unix-specific C-Kermit 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-Kermit Frequently Asked Questions: +.sp + http://www.columbia.edu/kermit/ckfaq.html +.SH OTHER TOPICS +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: +.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-Kermit is: +.TP +.I +Using C-Kermit +Frank da Cruz and Christine M. Gianone, +Second Edition, Digital Press / Butterworth-Heinemann, Woburn, MA, 1997, 622 +pages, ISBN 1-55558-164-1. This is a printed book. It covers C-Kermit 6.0. +.TP +The C-Kermit 7.0 Supplement +http://www.columbia.edu/kermit/ckermit2.html +.TP +The C-Kermit 8.0 Supplement +http://www.columbia.edu/kermit/ckermit3.html +.PP +Visit C-Kermit 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-Kermit FAQ (Frequently Asked Questions about C-Kermit) +.TP +http://www.columbia.edu/kermit/telnet.html +C-Kermit Telnet client documentation +.TP +http://www.columbia.edu/kermit/security.html +C-Kermit 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-Kermit 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-Kermit bugs, hints, tips. +.TP +ckubwr.txt +Unix-specific C-Kermit bugs, hints, tips. +.TP +ckcplm.txt +C-Kermit program logic manual. +.TP +ckccfg.txt +C-Kermit compile-time 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-present, with contributions from hundreds of others all over the +world. +.TP +Documentation +Frank da Cruz and Christine M Gianone +.TP +Address +.nf +The Kermit Project - Columbia Univerity +612 West 115th Street +New York NY 10025-7799 +USA +.fi +.TP +E-Mail +kermit@columbia.edu +.TP +Web +http://www.columbia.edu/kermit/ +.fi +.br diff --git a/ckupty.c b/ckupty.c new file mode 100644 index 0000000..705914c --- /dev/null +++ b/ckupty.c @@ -0,0 +1,1722 @@ +/* C K U P T Y -- C-Kermit pseudoterminal control functions for UNIX */ + +/* + 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 +*/ + +/* + 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 + . NeXTSTEP 3.x + . 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 + + Included but not tested yet in: + . Macintosh OSX, OpenBSD, and any other BSD44-based system not listed above + + 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.014, 20 Aug 2002"; + +/* These will no doubt need adjustment... */ + +#ifndef NEXT +#define HAVE_SETSID +#endif /* NEXT */ +#define HAVE_KILLPG +#define HAVE_TTYNAME +#define HAVE_WAITPID + +#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 */ + +extern int ttyfd; /* Standard Kermit usage */ +static int msg = 0; + +/* 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() { + int rc = 0; + 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() { /* Only make the necessary changes. */ +#ifndef USE_TERMIO + 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 */ + if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) { + int x; + errno = 0; +#ifdef INIT_SPTY + x = tcsetattr(spty, TCSANOW, &termbuf); + debug(F111,"set_termbuf tcsetattr(spty)",ckitoa(x),errno); +#else + 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 TIOCSTTY 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))) + 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; + + debug(F100,"HAVE_OPENPTY","",0); + if (openpty(fd, + &slavefd, + slave, + (struct termios *)0, + (struct winsize *)0 + ) + ) + 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: + if (*fd >= 0) { +#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 + /* XXX 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]; + *fd = open(slavebuf, O_RDWR|O_NDELAY); + if (*fd < 0) + continue; + debug(F110,"pty_getpty() found pty master",slavebuf,0); + slavebuf[sizeof("/dev/") - 1] = 't'; /* got pty */ + if (strlen(slavebuf) > slavelength -1) { + close(*fd); + *fd = -1; + return(PTY_GETPTY_SLAVE_TOOLONG); + } + debug(F110,"pty_getpty() slavebuf [2]",slavebuf,0); + ckstrncpy(slave, slavebuf, slavelength); + 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 TIOCSTTY 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) char * slave; int *fd; { + 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); +#endif /* MUST_SETPGRP */ + + errno = 0; + *fd = open(slave, O_RDWR); + if (*fd < 0) { + debug(F111,"pty_open_ctty() open failure", slave, errno); + return(PTY_OPEN_SLAVE_OPENFAIL); + } +#ifdef DEBUG + else if (deblog) { + debug(F110, "pty_open_ctty() open ok", slave, 0); + } +#endif /* DEBUG */ + +#ifdef MUST_SETPGRP + setpgrp(0, getpid()); +#endif /* MUST_SETPGRP */ + +#ifdef TIOCSCTTY + errno = 0; + retval = ioctl(*fd, TIOCSCTTY, 0); /* Don't check return.*/ + debug(F111,"pty_open_ctty() ioctl TIOCSCTTY",ckitoa(retval),errno); +#endif /* TIOCSTTY */ + return(0L); +} + +long +pty_open_slave(slave, fd) char *slave; int *fd; { + 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)) != 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); + if (retval != 0) { + debug(F111,"pty_open_slave()","pty_open_ctty() failed",retval); + return(PTY_OPEN_SLAVE_OPENFAIL); + } + retval = pty_initialize_slave(*fd); + if (retval) { + debug(F111,"pty_open_slave()","pty_initialize_slave() failed",retval); + return(retval); + } +#ifndef NO_DEVTTY + errno = 0; + testfd = open("/dev/tty", O_RDWR|O_NDELAY); + if (testfd < 0) { + debug(F111,"pty_open_slave() open failed","/dev/tty",errno); + close(*fd); + *fd = -1; + return(PTY_OPEN_SLAVE_NOCTTY); + } + close(testfd); +#endif /* NO_DEVTTY */ + debug(F110,"pty_open_slave()","success",0); + 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 */ + +static char Xline[17] = { 0, 0 }; +int pty_fork_pid = -1; + +/* + getptyslave() + Open the slave side of the pty, and do any initialization that is necessary. + The return value is a file descriptor for the slave side. +*/ +int +getptyslave() { + int t = -1; + long retval; +#ifdef TIOCGWINSZ + struct winsize ws; + extern int cmd_rows, cmd_cols; +#endif /* TIOCGWINSZ */ + + debug(F100,"getptyslave()","",0); + + /* + * 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)) != 0) { + perror(Xline); + msg++; + debug(F111,"getptyslave()","Unable to open slave",retval); + return(-1); + } + + debug(F111,"getptyslave","ttyfd",ttyfd); + debug(F111,"getptyslave","t",t); +#ifdef INIT_SPTY + spty = t; +#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. */ + init_termbuf(); +#ifdef TIOCGWINSZ + if (cmd_rows || cmd_cols) { + memset((char *)&ws, 0, sizeof(ws)); + ws.ws_col = cmd_cols; + ws.ws_row = cmd_rows; + ioctl(t, TIOCSWINSZ, (char *)&ws); + } +#endif /* TIOCGWINSZ */ + + /* 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_termbuf(); /* Set the tty modes, and make this our controlling tty. */ + + if (t != 0) + dup2(t, 0); + if (t != 1) + dup2(t, 1); + if (t != 2) + dup2(t, 2); + if (t > 2) + close(t); + + if (ttyfd > 2) { + close(ttyfd); + ttyfd = -1; + } + 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; + execvp(args[0],args); +} + +/* Get a pty, scan input lines. */ + +int +do_pty(cmd) char * cmd; { + long retval; + int syncpipe[2]; + int i; +#ifdef HAVE_PTYTRAP + int x; +#endif /* HAVE_PTYTRAP */ + + 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); + return(-1); + } + debug(F110,"do_pty() Xline",Xline,0); + +#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 */ + 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 { + debug(F110,"do_pty()","Slave starts",0); + if (getptyslave() == 0) { +#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]); + + exec_cmd(cmd); + debug(F111,"do_pty()","exec_cmd() returns - why?",errno); + } + debug(F110,"do_pty()","getptyslave() fails - exiting",0); + exit(1); + } + return(0); +} /* end of do_pty() */ + + +VOID +end_pty() { + msg = 0; /* Message counter */ + if (Xline[0] && pty_fork_pid >= 0) { + pty_cleanup(Xline,pty_fork_pid,1); + Xline[0] = '\0'; + pty_fork_pid = -1; + } +} +#endif /* NETPTY */ diff --git a/ckupty.h b/ckupty.h new file mode 100644 index 0000000..6749bec --- /dev/null +++ b/ckupty.h @@ -0,0 +1,174 @@ +/* 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 +#include +#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 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 *)); +_PROTOTYP(long pty_open_ctty, (char *, 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__ */ diff --git a/ckuscr.c b/ckuscr.c new file mode 100644 index 0000000..98d2fd6 --- /dev/null +++ b/ckuscr.c @@ -0,0 +1,688 @@ +#include "ckcsym.h" + +#ifndef NOICP +#ifndef NOSCRIPT +char *loginv = "Script Command, 8.0.032, 20 Dec 2001"; + +/* C K U S C R -- expect-send script implementation */ + +/* + Copyright (C) 1985, 2004, + 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 */ diff --git a/ckusig.c b/ckusig.c new file mode 100644 index 0000000..9f60b96 --- /dev/null +++ b/ckusig.c @@ -0,0 +1,352 @@ +/* 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, 2004, + 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); +} diff --git a/ckusig.h b/ckusig.h new file mode 100644 index 0000000..bf61e38 --- /dev/null +++ b/ckusig.h @@ -0,0 +1,79 @@ +/* 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, 2004, + 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 */ + diff --git a/ckutio.c b/ckutio.c new file mode 100644 index 0000000..4c517cc --- /dev/null +++ b/ckutio.c @@ -0,0 +1,14492 @@ +#ifdef aegis +char *ckxv = "Aegis Communications support, 8.0.303, 17 Apr 2004"; +#else +#ifdef Plan9 +char *ckxv = "Plan 9 Communications support, 8.0.303, 17 Apr 2004"; +#else +char *ckxv = "UNIX Communications support, 8.0.303, 17 Apr 2004"; +#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, 2004, + 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 */ + +/* 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 */ + +#include /* System error numbers */ + +#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 */ +/* + 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 */ +#undef MINIX /* Old minix 1.0: used sgtty interface */ +#define BSD44ORPOSIX +#define SVORPOSIX +#define DCLTIMEVAL +#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 */ + +/* UUCP lockfile material... */ + +#ifndef NOUUCP +#ifdef USETTYLOCK +#ifdef HAVE_BAUDBOY /* Red Hat baudboy/lockdev */ +#include +#else +#ifdef USE_UU_LOCK +#ifdef __FreeBSD__ +#include /* FreeBSD */ +#else +#include /* OpenBSD */ +#endif /* HAVE_BAUDBOY */ +#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 */ +#ifdef FIONREAD +#undef FIONREAD +#endif /* FIONREAD */ +#endif /* XENIX */ + +/* 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 */ + +#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 */ + +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 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( 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 itslef */ +/* #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"; + hexdump(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"; + hexdump(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 */ +} + + +#ifndef NOSIGWINCH +#ifdef SIGWINCH +SIGTYP +winchh(foo) int foo; { /* SIGWINCH handler */ + int x = 0; +#ifdef NETPTY + extern int pty_fork_pid; +#endif /* NETPTY */ +#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 */ + +#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(ttname); + 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); + } + } + /* 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 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. */ + if (!strncmp(ttdev,"/dev/",5) && ttdev[5]) + ckstrncpy(lockname,ttdev+5,DEVNAMLEN); + 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 { /* 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 */ + 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 */ + +#ifdef MINIX2 /* Hack alert */ +#define MINIX /* Use pre-2.0 speed selection for Minix 2.0 as well */ +#endif /* MINIX2 */ + + /* 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 */ + +#ifdef MINIX2 +/* End of hack to make MINIX2 use MINIX1 speed setting */ +#undef MINIX +#endif /* MINIX2 */ + +#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 92160: 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. + * + * 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. + * + * myunrd() is currently not called from anywhere inside kermit... + */ +#ifdef COMMENT /* not used */ +myunrd(ch) CHAR ch; { + if (my_item >= 0) { + mybuf[my_item--] = ch; + ++my_count; + } +} +#endif /* COMMENT */ + +/* 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 */ + if (deblog) hexdump("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(F100,"SVORPOSIX myfillbuf calling read()","",0); +#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; + 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) { + if ((n = krb4_des_read(ttyfd,mybuf,sizeof(mybuf))) < 0) + return(-3); + else + return(n); + } +#endif /* RLOGCODE */ +#endif /* KRB4 */ +#ifdef KRB5 +#ifdef RLOGCODE + if (ttnproto == NP_EK5LOGIN) { + if ((n = krb5_des_read(ttyfd,mybuf,sizeof(mybuf),0)) < 0) + return(-3); + else + return(n); + } +#endif /* RLOGCODE */ +#ifdef KRB5_U2U + if (ttnproto == NP_K5U2U) { + if ((n = krb5_u2u_read(ttyfd,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) { + if (pty_trap_handler(ttyfd) > 0) { + ttclos(0); + return(-3); + } + } +#endif /* HAVE_PTYTRAP */ +#endif /* NETPTY */ + n = read(fd, mybuf, sizeof(mybuf)); + debug(F101,"SVORPOSIX myfillbuf","",n); + debug(F101,"SVORPOSIX myfillbuf ttcarr","",ttcarr); + debug(F101,"SVORPOSIX myfillbuf errno","",errno); + 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) + hexdump("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 */ + +#ifdef MINIX2 +#undef MINIX +#endif /* MINIX2 */ + +/* 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 duplex; + 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(&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 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 MINIX2 +#undef BSD44ORPOSIX +#endif /* MINIX2 */ + +/* 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 */ + +#ifdef MINIX2 +#define BSD44ORPOSIX +#endif /* MINIX2 */ + + 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 */ + +/* 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) hexdump("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 + if (deblog) hexdump("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,s,n,0)); + } else +#endif /* RLOGCODE */ +#ifdef KRB5_U2U + if (ttnproto == NP_K5U2U) { + return(krb5_u2u_write(ttyfd,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,&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 communication line, 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 characters that were input are copied into "dest" with their parity bits + stripped if parity was selected. Returns the number of characters read. + Characters 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(). + + 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. +*/ + +static int csave = -1; + +#ifndef NOXFER +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 /* __SDTC__ */ +#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. */ + + 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 + /* debug(F101,"ttinl i","",i); */ + errno = 0; + if (csave > -1) { + n = csave; + debug(F101,"ttinl unsaving","",n); + } else +#ifdef COMMENT + if (xlocal && conchk() > 0) { + /* Here we could catch keyboard interruptions. */ + /* But this would be VERY expensive. */ + /* We could also do it in myread() but it would be */ + /* expensive there too -- even if done with select()... */ + } +#endif /* COMMENT */ + if ((n = myread()) < 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?) */ + + if (csave > -1) /* Char saved from last time */ + ch = csave; + else if ((n = read(fd, &ch, 1)) < 1) + break; /* Error - break out of while loop */ + n = ch; + +#endif /* MYREAD */ + + /* Get here with char in n */ + +#ifdef CK_ENCRYPTION + /* If csave > -1 we already decrypted this character */ + /* So don't decrypt it again */ + if (TELOPT_U(TELOPT_ENCRYPTION) && csave == -1) { + CHAR ch = n; + ck_tn_decrypt(&ch,1); + n = ch; + } +#endif /* CK_ENCRYPTION */ + + csave = -1; /* Unflag that we unsaved a char */ + +#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 + if (flag == 0) { /* Find the Start-Of-Packet. */ + if ((n & sopmask) == start) { /* Got it */ + flag = 1; + } else { /* Keep looking... */ + 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... +*/ + if (!havelen) { + if (i == 2) { + pktlen = xunchar(dest[1] & 0x7f); + if (pktlen > 1) { + havelen = 1; + debug(F101,"ttinl length","",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 ( +#ifdef PARSENSE +/* + 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) */ +#else /* !PARSENSE */ +/* + Built without PARSENSE, so just look for packet terminator. +*/ + ((n & 0x7f) == eol) +#endif /* PARSENSE */ + ) { +#ifndef PARSENSE + debug(F101,"ttinl got eol","",eol); /* (or turn) */ + dest[i] = '\0'; /* Yes, terminate the string, */ + /* debug(F101,"ttinl i","",i); */ +#else +#ifdef DEBUG + if (deblog) { + if ((n & 0x7f) != eol) { + debug(F101,"ttinl EOP length","",pktlen); + debug(F101,"ttinl i","",i); +#ifdef MYREAD +#ifdef PARSENSE +/* + We read a packet based on its length. This leaves the EOP character still + unread, and so ttchk() will always return at least 1 because of this. But + if we know it is there, we can safely get rid of it. So... +*/ + { + int x; + while (my_count > 0) { + x = ttinc(0); + /* Start of next packet */ + if (x == start) { /* Save for next time */ + csave = (unsigned)((unsigned)x & 0xff); + debug(F000,"ttinl csaved","",x); + break; + } + debug(F000,"ttinl removed","",x); + } + } +#endif /* PARSENSE */ +#endif /* MYREAD */ + + } else debug(F101,"ttinl got eol","",eol); /* (or turn) */ + } +#endif /* DEBUG */ + 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 = ttprty; + needpchk = 0; + } +#endif /* PARSENSE */ + if (timo) { /* Turn off timer. */ + ttimoff(); + } +#ifdef COMMENT + debug(F011,"ttinl got", dest, (i < 60) ? i : -60); +#else /* COMMENT */ + hexdump("ttinl got",dest,i); +#endif /* COMMENT */ +#ifdef STREAMING + /* ttinl() was called because there was non-packet */ + /* data sitting int the 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(&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(&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; + 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 NONOSETBUF + if (x > -1) { + setbuf(stdout,NULL); /* Make console unbuffered. */ + debug(F100,"concb setbuf A","",0); + } +#else +#ifndef aegis +#ifndef NOSETBUF + if (x > -1) { + setbuf(stdout,NULL); /* Make console unbuffered. */ + debug(F100,"concb setbuf B","",0); + } +#endif /* NOSETBUF */ +#endif /* aegis */ +#endif /* NONOSETBUF */ + +#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 = 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(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(&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(&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 COHERENT +#ifndef NOGETID_PROTOS +extern UID_T getuid(), geteuid(), getreuid(); +extern GID_T getgid(), getegid(), getregid(); +#endif /* NOGETID_PROTOS */ +#else +extern UID_T getreuid(); +extern GID_T getregid(); +#endif /* COHERENT */ +#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_BAUDBOY + + /* 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_BAUDBOY */ + 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_BAUDBOY + 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_BAUDBOY */ + 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_BAUDBOY + if (privuid != (UID_T) -1) + if (switchuid(privuid,realuid)) + err |= 1; + + if (privgid != (GID_T) -1) + if (switchgid(privgid,realgid)) + err |= 2; +#endif /* HAVE_BAUDBOY */ + 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_BAUDBOY +#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_BAUDBOY */ +} + +/* 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); */ + } +} + +/* T T R U N C M D -- Redirect an external command over the connection. */ + +#ifdef CK_REDIR +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); + } + 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 */ +#include +#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. We fake one here using time(2) */ + +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); +} + +/* Minix does not support symbolic links. We implement a version of + readlink that always fails */ + +int +readlink(const char *path, void *buf, size_t bufsiz) { + errno = ENOSYS; + return(-1); +} +#endif /* MINIX2 */ diff --git a/ckututor.txt b/ckututor.txt new file mode 100644 index 0000000..9448126 --- /dev/null +++ b/ckututor.txt @@ -0,0 +1,1959 @@ + +C-KERMIT 8.0 UNIX MANUAL PAGE AND TUTORIAL + + Frank da Cruz, Christine M. Gianone + [1]The Kermit Project, [2]Columbia University + + [ [3]PDF version ] [ [4]Nroff version ] + + This document is intended to give the beginner sufficient + information to make basic (if not advanced) use of C-Kermit 8.0. + Although it might be rather long for a Unix manual page (about 1600 + lines), it's still far shorter than the 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. + + Most recent update: 24 October 2002 + ________________________________________________________________________ + + CONTENTS + * [5]DESCRIPTION + * [6]SYNOPSIS + * [7]OPTIONS + * [8]COMMAND LANGUAGE + * [9]INITIALIZATION FILE + * [10]MODES OF OPERATION + * [11]MAKING CONNECTIONS + * [12]TRANSFERRING FILES WITH KERMIT + * [13]KERMIT CLIENT/SERVER CONNECTIONS + * [14]KERMIT'S BUILT-IN FTP AND HTTP CLIENTS + * [15]INTERNET KERMIT SERVICE + * [16]SECURITY + * [17]ALTERNATIVE COMMAND-LINE PERSONALITIES + * [18]LICENSE + * [19]OTHER TOPICS + * [20]DOCUMENTATION AND UPDATES + * [21]FILES + * [22]AUTHORS + _________________________________________________________________ + + DESCRIPTION [ [23]Top ] [ [24]Contents ] [ [25]Next ] + + [26]C-Kermit is an all-purpose communications software package from + the [27]Kermit Project at [28]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 [[29]RFC2839, [30]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 8.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 [31]DOCUMENTATION + below). For further information about C-Kermit, Kermit software for + other platforms, and Kermit manuals, visit the Kermit Project website: + + [32]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. + + [ [33]Kermit Home ] [ [34]C-Kermit Home ] [ [35]C-Kermit FAQ ] + ________________________________________________________________________ + + SYNOPSIS [ [36]Top ] [ [37]Contents ] [ [38]Next ] [ [39]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 [40]below). A second command-line format allows the one and + only argument to be a [41]Telnet, FTP, HTTP, or IKSD URL. + + Order of execution: + + 1. [42]The command file (if any). + 2. [43]The initialization file, if any, unless suppressed with -Y. + 3. [44]The customization file (if it is executed by the + initialization file). + 4. [45]The command-line URL (if any, and if so, execution stops + here). + 5. [46]Command-line options (if any). + 6. [47]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. + + [ [48]Kermit Home ] [ [49]C-Kermit Home ] [ [50]C-Kermit FAQ ] + ________________________________________________________________________ + + OPTIONS [ [51]Top ] [ [52]Contents ] [ [53]Next ] [ [54]Previous ] + + 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 + [55]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 8.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. [56]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. [57]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). + Equivalanet 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 [58]IKSD-Only options. + + See the [59]file-transfer section for examples of command-line + invocation. + ________________________________________________________________________ + + COMMAND LANGUAGE [ [60]Top ] [ [61]Contents ] [ [62]Next ] [ [63]Previous ] + + * [64]Command Files, Macros, and Scripts + * [65]Command List + + C-Kermit's interactive command language is the subject of a + [66]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 the definition + to be in effect automatically every time you start Kermit, put the + definition in your initialization or customization file (explained + [67]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 interupted 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 interupted 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 [68]manual and supplements, and/or + visit the [69]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 + [70]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'. + + [ [71]Kermit Home ] [ [72]C-Kermit Home ] [ [73]C-Kermit FAQ ] + ________________________________________________________________________ + + INITIALIZATION FILE [ [74]Top ] [ [75]Contents ] [ [76]Next ] [ [77]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. + + [ [78]Kermit Home ] [ [79]C-Kermit Home ] [ [80]C-Kermit FAQ ] + ________________________________________________________________________ + + MODES OF OPERATION [ [81]Top ] [ [82]Contents ] [ [83]Next ] [ [84]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? [85]CLICK HERE to + read about it. + + [ [86]Kermit Home ] [ [87]C-Kermit Home ] [ [88]C-Kermit FAQ ] + ________________________________________________________________________ + + MAKING CONNECTIONS [ [89]Top ] [ [90]Contents ] [ [91]Next ] [ [92]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 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 [93]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) 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, + [94]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 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 [95]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. + + [ [96]Kermit Home ] [ [97]C-Kermit Home ] [ [98]C-Kermit FAQ ] + ________________________________________________________________________ + + TRANSFERRING FILES WITH KERMIT [ [99]Top ] [ [100]Contents ] [ [101]Next ] [ + [102]Previous ] + + * [103]Downloading Files + * [104]Uploading Files + * [105]Kermit Transfers the Old-Fashioned Way + * [106]If File Transfer Fails + * [107]Advanced Kermit File Transfer Features + * [108]Non-Kermit File Transfer + + There is a [109]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 [110]C-Kermit (7.0 or later) and [111]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 [112]Kermit 95, [113]C-Kermit, or + [114]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 + [115]Kermit Project partners are recommended because they + contain the best Kermit protocol implementations and because + [116]we can support them in case of trouble. + + If you still have trouble, consult Chapter 10 of [117]Using + C-Kermit, or send email to [118]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 [119]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; [120]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). + + [ [121]Kermit Home ] [ [122]C-Kermit Home ] [ [123]C-Kermit FAQ ] + ________________________________________________________________________ + + KERMIT CLIENT/SERVER CONNECTIONS [ [124]Top ] [ [125]Contents ] [ [126]Next ] + [ [127]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 ([128]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. + + [ [129]Kermit Home ] [ [130]C-Kermit Home ] [ [131]C-Kermit FAQ ] + ________________________________________________________________________ + + KERMIT'S BUILT-IN FTP AND HTTP CLIENTS [ [132]Top ] [ [133]Contents ] [ + [134]Next ] [ [135]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 [136]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: + + [137]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 + [138]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. + + [ [139]Kermit Home ] [ [140]C-Kermit Home ] [ [141]C-Kermit FAQ ] [ + [142]FTP Client ] [ [143]HTTP Client ] + ________________________________________________________________________ + + INTERNET KERMIT SERVICE [ [144]Top ] [ [145]Contents ] [ [146]Next ] [ + [147]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: + + [148]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: + + [149]http://www.columbia.edu/kermit/iksd.html + + [ [150]Kermit Home ] [ [151]C-Kermit Home ] [ [152]C-Kermit FAQ ] + ________________________________________________________________________ + + SECURITY [ [153]Top ] [ [154]Contents ] [ [155]Next ] [ [156]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: + + [157]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. + + [ [158]Kermit Home ] [ [159]C-Kermit Home ] [ [160]C-Kermit FAQ ] + ________________________________________________________________________ + + ALTERNATIVE COMMAND-LINE PERSONALITIES [ [161]Top ] [ [162]Contents ] [ + [163]Next ] [ [164]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 + [165]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 [166]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 pag +e + + [ [167]Kermit Home ] [ [168]C-Kermit Home ] [ [169]C-Kermit FAQ ] + ________________________________________________________________________ + + LICENSE [ [170]Top ] [ [171]Contents ] [ [172]Next ] [ [173]Previous ] + + C-Kermit has an unusual license, but a fair and sensible one given + that 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-Kermit 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-Kermit or bundle it with a product or otherwise distribute it + in a commercial setting EXCEPT WITH AN OPEN-SOURCE 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-Kermit 7.0 + or later, or download it from + [174]ftp://kermit.columbia.edu/kermit/c-kermit/COPYING.TXT. Send + licensing inquiries to [175]kermit@columbia.edu. + + [ [176]Kermit Home ] [ [177]C-Kermit Home ] [ [178]C-Kermit FAQ ] + ________________________________________________________________________ + + OTHER TOPICS [ [179]Top ] [ [180]Contents ] [ [181]Next ] [ [182]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: + + [183]http://www.columbia.edu/kermit/ + + There you will also find [184]Kermit software packages for other + platforms: different Unix varieties, Windows, DOS, VMS, IBM + mainframes, and many others: 20+ years' worth. + + [ [185]Kermit Home ] [ [186]C-Kermit Home ] [ [187]C-Kermit FAQ ] + ________________________________________________________________________ + + DOCUMENTATION AND UPDATES [ [188]Top ] [ [189]Contents ] [ [190]Next ] [ + [191]Previous ] + + The manual for C-Kermit is: + + 1. Frank da Cruz and Christine M. Gianone, [192]Using C-Kermit, + Second Edition, Digital Press / Butterworth-Heinemann, Woburn, MA, + 1997, 622 pages, ISBN 1-55558-164-1. This is a printed book. It + covers C-Kermit 6.0. + 2. The C-Kermit 7.0 Supplement: + [193]http://www.columbia.edu/kermit/ckermit70.html + 3. The C-Kermit 8.0 Supplement: + [194]http://www.columbia.edu/kermit/ckermit80.html + + The C-Kermit home page is here: + + [195]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 [196]prebuilt binaries for many platforms. Also + visit: + + [197]http://www.columbia.edu/kermit/scriptlib.html + The Kermit script library and tutorial + + [198]http://www.columbia.edu/kermit/newfaq.html + The Kermit FAQ (Frequently Asked Questions about Kermit) + + [199]http://www.columbia.edu/kermit/ckfaq.html + The C-Kermit FAQ (Frequently Asked Questions about C-Kermit) + + [200]http://www.columbia.edu/kermit/security.html + The Kermit security reference. + + [201]http://www.columbia.edu/kermit/telnet.html + C-Kermit Telnet client documentation. + + [202]http://www.columbia.edu/kermit/studies.html + Case studies. + + [203]http://www.columbia.edu/kermit/ckcbwr.html + General C-Kermit Hints and Tips. + + [204]http://www.columbia.edu/kermit/ckubwr.html + Unix C-Kermit Hints and Tips. + + [205]http://www.columbia.edu/kermit/ckvbwr.html + VMS C-Kermit Hints and Tips. + + [206]http://www.columbia.edu/kermit/ckuins.html + Unix C-Kermit Installation Instructions + + [207]http://www.columbia.edu/kermit/ckvins.html + VMS C-Kermit Installation Instructions + + [208]http://www.columbia.edu/kermit/support.html + Technical support. + + [209]http://www.columbia.edu/kermit/k95tutorial.html + Kermit 95 tutorial (this document). + + [210]comp.protocols.kermit.misc + The Kermit newsgroup (unmoderated). + + [ [211]Kermit Home ] [ [212]C-Kermit Home ] [ [213]C-Kermit FAQ ] + ________________________________________________________________________ + + FILES [ [214]Top ] [ [215]Contents ] [ [216]Next ] [ [217]Previous ] + + [218]COPYING.TXT + C-Kermit license. + + [219]~/.kermrc + Initialization file. + + [220]~/.mykermrc + Customization file. + + ~/.kdd + Kermit dialing directory (see manual). + + ~/.knd + Kermit network directory (see manual). + + ~/.ksd + Kermit services directory (see manual). + + [221]ckuins.html + Installation instructions for Unix. + + [222]ckcbwr.html + General C-Kermit bugs, hints, tips. + + [223]ckubwr.html + Unix-specific C-Kermit bugs, hints, tips. + + [224]ckcplm.html + C-Kermit program logic manual. + + [225]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 [226]installation + instructions). + + [ [227]Kermit Home ] [ [228]C-Kermit Home ] [ [229]C-Kermit FAQ ] + ________________________________________________________________________ + + AUTHORS [ [230]Top ] [ [231]Contents ] [ [232]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 8.0 Unix Manual Page and Tutorial / + [233]kermit@columbia.edu / 24 October 2002 + +References + + 1. http://www.columbia.edu/kermit/ + 2. http://www.columbia.edu/ + 3. http://www.columbia.edu/kermit/ckututor.pdf + 4. ftp://kermit.columbia.edu/kermit/test/text/ckuker.nr + 5. http://www.columbia.edu/kermit/ckututor.html#description + 6. http://www.columbia.edu/kermit/ckututor.html#synopsis + 7. http://www.columbia.edu/kermit/ckututor.html#options + 8. http://www.columbia.edu/kermit/ckututor.html#commands + 9. http://www.columbia.edu/kermit/ckututor.html#initfile + 10. http://www.columbia.edu/kermit/ckututor.html#modes + 11. http://www.columbia.edu/kermit/ckututor.html#connections + 12. http://www.columbia.edu/kermit/ckututor.html#transfer + 13. http://www.columbia.edu/kermit/ckututor.html#server + 14. http://www.columbia.edu/kermit/ckututor.html#ftp + 15. http://www.columbia.edu/kermit/ckututor.html#iksd + 16. http://www.columbia.edu/kermit/ckututor.html#security + 17. http://www.columbia.edu/kermit/ckututor.html#personae + 18. http://www.columbia.edu/kermit/ckututor.html#license + 19. http://www.columbia.edu/kermit/ckututor.html#other + 20. http://www.columbia.edu/kermit/ckututor.html#documentation + 21. http://www.columbia.edu/kermit/ckututor.html#files + 22. http://www.columbia.edu/kermit/ckututor.html#authors + 23. http://www.columbia.edu/kermit/ckututor.html#top + 24. http://www.columbia.edu/kermit/ckututor.html#contents + 25. http://www.columbia.edu/kermit/ckututor.html#synopsis + 26. http://www.columbia.edu/kermit/ckermit.html + 27. http://www.columbia.edu/kermit/ + 28. http://www.columbia.edu/ + 29. ftp://ftp.isi.edu/in-notes/rfc2839.txt + 30. ftp://ftp.isi.edu/in-notes/rfc2840.txt + 31. http://www.columbia.edu/kermit/ckututor.html#documentation + 32. http://www.columbia.edu/kermit/ + 33. http://www.columbia.edu/kermit/ + 34. http://www.columbia.edu/kermit/ckermit.html + 35. http://www.columbia.edu/kermit/ckfaq.html + 36. http://www.columbia.edu/kermit/ckututor.html#top + 37. http://www.columbia.edu/kermit/ckututor.html#contents + 38. http://www.columbia.edu/kermit/ckututor.html#options + 39. http://www.columbia.edu/kermit/ckututor.html#synopsis + 40. http://www.columbia.edu/kermit/ckututor.html#kerbang + 41. http://www.columbia.edu/kermit/ckututor.html#personae + 42. http://www.columbia.edu/kermit/ckututor.html#kerbang + 43. http://www.columbia.edu/kermit/ckututor.html#initfile + 44. http://www.columbia.edu/kermit/ckututor.html#initfile + 45. http://www.columbia.edu/kermit/ckututor.html#personae + 46. http://www.columbia.edu/kermit/ckututor.html#options + 47. http://www.columbia.edu/kermit/ckututor.html#commands + 48. http://www.columbia.edu/kermit/ + 49. http://www.columbia.edu/kermit/ckermit.html + 50. http://www.columbia.edu/kermit/ckfaq.html + 51. http://www.columbia.edu/kermit/ckututor.html#top + 52. http://www.columbia.edu/kermit/ckututor.html#contents + 53. http://www.columbia.edu/kermit/ckututor.html#commands + 54. http://www.columbia.edu/kermit/ckututor.html#description + 55. http://www.columbia.edu/kermit/ckututor.html#commands + 56. http://www.columbia.edu/kermit/ckututor.html#personae + 57. http://www.columbia.edu/kermit/ckututor.html#personae + 58. http://www.columbia.edu/kermit/ckututor.html#iksd + 59. http://www.columbia.edu/kermit/ckututor.html#transfer + 60. http://www.columbia.edu/kermit/ckututor.html#top + 61. http://www.columbia.edu/kermit/ckututor.html#contents + 62. http://www.columbia.edu/kermit/ckututor.html#initfile + 63. http://www.columbia.edu/kermit/ckututor.html#options + 64. http://www.columbia.edu/kermit/ckututor.html#kerbang + 65. http://www.columbia.edu/kermit/ckututor.html#cmdlist + 66. http://www.columbia.edu/kermit/ckututor.html#documentation + 67. http://www.columbia.edu/kermit/ckututor.html#initfile + 68. http://www.columbia.edu/kermit/ckututor.html#documentation + 69. http://www.columbia.edu/kermit/ckscripts.html + 70. http://www.columbia.edu/kermit/ckututor.html#documentation + 71. http://www.columbia.edu/kermit/ + 72. http://www.columbia.edu/kermit/ckermit.html + 73. http://www.columbia.edu/kermit/ckfaq.html + 74. http://www.columbia.edu/kermit/ckututor.html#top + 75. http://www.columbia.edu/kermit/ckututor.html#contents + 76. http://www.columbia.edu/kermit/ckututor.html#modes + 77. http://www.columbia.edu/kermit/ckututor.html#commands + 78. http://www.columbia.edu/kermit/ + 79. http://www.columbia.edu/kermit/ckermit.html + 80. http://www.columbia.edu/kermit/ckfaq.html + 81. http://www.columbia.edu/kermit/ckututor.html#top + 82. http://www.columbia.edu/kermit/ckututor.html#contents + 83. http://www.columbia.edu/kermit/ckututor.html#connections + 84. http://www.columbia.edu/kermit/ckututor.html#initfile + 85. http://www.columbia.edu/kermit/ckfaq.html#term + 86. http://www.columbia.edu/kermit/ + 87. http://www.columbia.edu/kermit/ckermit.html + 88. http://www.columbia.edu/kermit/ckfaq.html + 89. http://www.columbia.edu/kermit/ckututor.html#top + 90. http://www.columbia.edu/kermit/ckututor.html#contents + 91. http://www.columbia.edu/kermit/ckututor.html#transfer + 92. http://www.columbia.edu/kermit/ckututor.html#modes + 93. http://www.columbia.edu/kermit/ckututor.html#iksd + 94. ftp://ftp.isi.edu/in-notes/rfc2217.txt + 95. http://www.columbia.edu/kermit/ckututor.html#ftp + 96. http://www.columbia.edu/kermit/ + 97. http://www.columbia.edu/kermit/ckermit.html + 98. http://www.columbia.edu/kermit/ckfaq.html + 99. http://www.columbia.edu/kermit/ckututor.html#top + 100. http://www.columbia.edu/kermit/ckututor.html#contents + 101. http://www.columbia.edu/kermit/ckututor.html#server + 102. http://www.columbia.edu/kermit/ckututor.html#connections + 103. http://www.columbia.edu/kermit/ckututor.html#download + 104. http://www.columbia.edu/kermit/ckututor.html#upload + 105. http://www.columbia.edu/kermit/ckututor.html#oldfashioned + 106. http://www.columbia.edu/kermit/ckututor.html#trouble + 107. http://www.columbia.edu/kermit/ckututor.html#advanced + 108. http://www.columbia.edu/kermit/ckututor.html#nonkermit + 109. http://www.columbia.edu/kermit/kermit.html#notslow + 110. http://www.columbia.edu/kermit/ckermit.html + 111. http://www.columbia.edu/kermit/k95.html + 112. http://www.columbia.edu/kermit/k95.html + 113. http://www.columbia.edu/kermit/ckermit.html + 114. http://www.columbia.edu/kermit/mskermit.html + 115. http://www.columbia.edu/kermit/ + 116. http://www.columbia.edu/kermit/support.html + 117. http://www.columbia.edu/kermit/ckmanual.html + 118. mailto:kermit-support@columbia.edu + 119. http://www.columbia.edu/kermit/ckututor.html#documentation + 120. http://www.columbia.edu/kermit/ckututor.html#ftp + 121. http://www.columbia.edu/kermit/ + 122. http://www.columbia.edu/kermit/ckermit.html + 123. http://www.columbia.edu/kermit/ckfaq.html + 124. http://www.columbia.edu/kermit/ckututor.html#top + 125. http://www.columbia.edu/kermit/ckututor.html#contents + 126. http://www.columbia.edu/kermit/ckututor.html#ftp + 127. http://www.columbia.edu/kermit/ckututor.html#transfer + 128. http://www.columbia.edu/kermit/ckututor.html#iksd + 129. http://www.columbia.edu/kermit/ + 130. http://www.columbia.edu/kermit/ckermit.html + 131. http://www.columbia.edu/kermit/ckfaq.html + 132. http://www.columbia.edu/kermit/ckututor.html#top + 133. http://www.columbia.edu/kermit/ckututor.html#contents + 134. http://www.columbia.edu/kermit/ckututor.html#iksd + 135. http://www.columbia.edu/kermit/ckututor.html#transfer + 136. http://www.columbia.edu/kermit/ckututor.html#server + 137. http://www.columbia.edu/kermit/ftpclient.html + 138. http://www.columbia.edu/kermit/ckututor.html#documentation + 139. http://www.columbia.edu/kermit/ + 140. http://www.columbia.edu/kermit/ckermit.html + 141. http://www.columbia.edu/kermit/ckfaq.html + 142. http://www.columbia.edu/kermit/ckermit3.html#x3 + 143. http://www.columbia.edu/kermit/ckermit3.html#x2.2 + 144. http://www.columbia.edu/kermit/ckututor.html#top + 145. http://www.columbia.edu/kermit/ckututor.html#contents + 146. http://www.columbia.edu/kermit/ckututor.html#security + 147. http://www.columbia.edu/kermit/ckututor.html#ftp + 148. http://www.columbia.edu/kermit/cuiksd.html + 149. http://www.columbia.edu/kermit/iksd.html + 150. http://www.columbia.edu/kermit/ + 151. http://www.columbia.edu/kermit/ckermit.html + 152. http://www.columbia.edu/kermit/ckfaq.html + 153. http://www.columbia.edu/kermit/ckututor.html#top + 154. http://www.columbia.edu/kermit/ckututor.html#contents + 155. http://www.columbia.edu/kermit/ckututor.html#personae + 156. http://www.columbia.edu/kermit/ckututor.html#iksd + 157. http://www.columbia.edu/kermit/security.html + 158. http://www.columbia.edu/kermit/ + 159. http://www.columbia.edu/kermit/ckermit.html + 160. http://www.columbia.edu/kermit/ckfaq.html + 161. http://www.columbia.edu/kermit/ckututor.html#top + 162. http://www.columbia.edu/kermit/ckututor.html#contents + 163. http://www.columbia.edu/kermit/ckututor.html#license + 164. http://www.columbia.edu/kermit/ckututor.html#iksd + 165. http://www.columbia.edu/kermit/ckututor.html#options + 166. http://www.columbia.edu/kermit/ckermit3.html#x3.1.2 + 167. http://www.columbia.edu/kermit/ + 168. http://www.columbia.edu/kermit/ckermit.html + 169. http://www.columbia.edu/kermit/ckfaq.html + 170. http://www.columbia.edu/kermit/ckututor.html#top + 171. http://www.columbia.edu/kermit/ckututor.html#contents + 172. http://www.columbia.edu/kermit/ckututor.html#other + 173. http://www.columbia.edu/kermit/ckututor.html#personae + 174. ftp://kermit.columbia.edu/kermit/c-kermit/COPYING.TXT + 175. mailto:kermit@columbia.edu + 176. http://www.columbia.edu/kermit/ + 177. http://www.columbia.edu/kermit/ckermit.html + 178. http://www.columbia.edu/kermit/ckfaq.html + 179. http://www.columbia.edu/kermit/ckututor.html#top + 180. http://www.columbia.edu/kermit/ckututor.html#contents + 181. http://www.columbia.edu/kermit/ckututor.html#documentation + 182. http://www.columbia.edu/kermit/ckututor.html#license + 183. http://www.columbia.edu/kermit/ + 184. http://www.columbia.edu/kermit/howtoget.html + 185. http://www.columbia.edu/kermit/ + 186. http://www.columbia.edu/kermit/ckermit.html + 187. http://www.columbia.edu/kermit/ckfaq.html + 188. http://www.columbia.edu/kermit/ckututor.html#top + 189. http://www.columbia.edu/kermit/ckututor.html#contents + 190. http://www.columbia.edu/kermit/ckututor.html#files + 191. http://www.columbia.edu/kermit/ckututor.html#other + 192. http://www.columbia.edu/kermit/ckmanual.html + 193. http://www.columbia.edu/kermit/ckermit70.html + 194. http://www.columbia.edu/kermit/ckermit80.html + 195. http://www.columbia.edu/kermit/ckermit.html + 196. http://www.columbia.edu/kermit/ck80binaries.html + 197. http://www.columbia.edu/kermit/scriptlib.html + 198. http://www.columbia.edu/kermit/newfaq.html + 199. http://www.columbia.edu/kermit/ckfaq.html + 200. http://www.columbia.edu/kermit/security.html + 201. http://www.columbia.edu/kermit/telnet.html + 202. http://www.columbia.edu/kermit/studies.html + 203. http://www.columbia.edu/kermit/ckcbwr.html + 204. http://www.columbia.edu/kermit/ckubwr.html + 205. http://www.columbia.edu/kermit/ckvbwr.html + 206. http://www.columbia.edu/kermit/ckuins.html + 207. http://www.columbia.edu/kermit/ckvins.html + 208. http://www.columbia.edu/kermit/support.html + 209. http://www.columbia.edu/kermit/k95tutorial.html + 210. news:comp.protocols.kermit.misc + 211. http://www.columbia.edu/kermit/ + 212. http://www.columbia.edu/kermit/ckermit.html + 213. http://www.columbia.edu/kermit/ckfaq.html + 214. http://www.columbia.edu/kermit/ckututor.html#top + 215. http://www.columbia.edu/kermit/ckututor.html#contents + 216. http://www.columbia.edu/kermit/ckututor.html#authors + 217. http://www.columbia.edu/kermit/ckututor.html#documentation + 218. ftp://kermit.columbia.edu/kermit/c-kermit/COPYING.TXT + 219. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit.ini + 220. ftp://kermit.columbia.edu/kermit/c-kermit/ckermod.ini + 221. http://www.columbia.edu/kermit/ckuins.html + 222. http://www.columbia.edu/kermit/ckcbwr.html + 223. http://www.columbia.edu/kermit/ckubwr.html + 224. http://www.columbia.edu/kermit/ckcplm.html + 225. http://www.columbia.edu/kermit/ckccfg.html + 226. http://www.columbia.edu/kermit/ckuins.html + 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#files + 233. mailto:kermit@columbia.edu diff --git a/ckuus2.c b/ckuus2.c new file mode 100644 index 0000000..1290763 --- /dev/null +++ b/ckuus2.c @@ -0,0 +1,13838 @@ +#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, 2004, + 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(). +*/ +#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 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. To order,", +"call +1 212 854-3703 or +1 800 366-2665.", +#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://www.columbia.edu/kermit/ckermit.html.", +#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 / Butterworth-Heinemann 1997, ISBN 1-55558-164-1.", +"To order: +1 212 854-3703 or +1 800 366-2665. More info at the Kermit", + +"Project website, http://www.columbia.edu/kermit/.", +"" +}; +#endif /* NOIKSD */ + +#ifndef NOHELP +char *newstxt[] = { +#ifdef OS2 +"Welcome to Kermit 95 2.1.3. Major new features include:", +#else +"Welcome to C-Kermit 8.0.206. Major new features include:", +#endif /* OS2 */ +#ifdef NT +#ifdef KUI +" . Runs in GUI window", +#else +" . GUI version available", +#endif /* KUI */ +#endif /* NT */ +#ifdef SSHBUILTIN +" . New built-in SSH v1 and v2 clients", +#endif /* SSHBUILTIN */ +#ifdef NEWFTP +" . A new built-in FTP client", +#endif /* NEWFTP */ +#ifndef NOHTTP +" . A new HTTP 1.1 client", +#endif /* NOHTTP */ +#ifdef TN_COMPORT +" . Telnet Com Port Option for dialing from Telnet modem servers", +#endif /* TN_COMPORT */ +" . File scanning for automatic text/binary determination", +#ifdef CKLEARN +#ifndef OS2 +" . Learned scripts", +#endif /* OS2 */ +#endif /* CKLEARN */ +#ifndef NOSPL +#ifndef NOSEXP +" . LISP-like S-Expressions and natural floating-point arithmetic", +#endif /* NOSEXP */ +" . Lots of script programming improvements", +#endif /* NOSPL */ +" . Performance improvements and bug fixes", +" ", +"Documentation:", +" 1. \"Using C-Kermit\", second edition (1997), current with C-Kermit 6.0.", +" 2. http://www.columbia.edu/kermit/ckermit70.html", +" which documents the new features of C-Kermit 7.0.", +" 3. http://www.columbia.edu/kermit/ckermit80.html", +" which documents the new features of C-Kermit 8.0.", +" ", +"If the release date shown by the VERSION command is long past, be sure to", +"check with the Kermit Project to see if there have been updates.", +"" +}; +#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 */ + +" . ? (question mark) Display a menu for the current command field." +, +" . ESC (or TAB) 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 */ +" ", +#ifdef OS2 +"For a Kermit 95 tutorial, visit:", +" http://www.columbia.edu/kermit/k95tutor.html", +" ", +#endif /* OS2 */ +"For a C-Kermit tutorial, visit:", +" http://www.columbia.edu/kermit/ckututor.html", +" ", +"To learn about script programming and automation:", +" Read the manual, \"Using C-Kermit\". For a brief tutorial, visit:", +" http://www.columbia.edu/kermit/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, contact:", +" ", +" The Kermit Project E-mail: kermit@columbia.edu", +" Columbia University Web: http://www.columbia.edu/kermit/", +" 612 West 115th Street Voice: +1 (212) 854-3703", +" New York NY 10025-7799 Fax: +1 (212) 662-6442", +" USA", +"" +}; + +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.", +"" +}; + +#ifdef OS2 +#ifdef KUI +static char * hmxygui[] = { +"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 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 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 */ + +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[] = { +"\\fword(s1,n1,s2,s3,n2,n3) - Extract word from string.", +" s1 = source string", +" n1 = word number (1-based)", +" s2 = optional break set.", +" s3 = optional include set.", +" n2 = optional grouping mask.", +" n3 = optional separator flag:", +" 0 = collapse adjacent separators", +" 1 = don't collapse adjacent separators.", +" ", +" Default break set is all characters except ASCII letters and digits.", +" ASCII (C0) control characters are always treated as break characters.", +" Default include set is null.", +" ", +" If grouping mask given and 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 n, if there is one, otherwise an empty string.", +"" +}; + +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 [ /NOMATCH ] { number-of-seconds, time-of-day } [ text ]", +"Example: INPUT 5 Login: or INPUT 23:59:59 RING", +" Waits up to the given number of seconds, or until the given time of day,", +" for the given text to arrive on the connection. If no text is given,", +" INPUT waits for any character. 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.", +" For use in script programs with IF FAILURE and IF SUCCESS. Also see", +" MINPUT, REINPUT, SET INPUT. See HELP PAUSE for details on time-of-day", +" format. The text, if given, can be a \\pattern() invocation, in which", +" case it is treated as a pattern rather than a literal string (HELP", +" PATTERNS for details).", +""}; + +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 \"%\" 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 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.", +" ", +"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.", +#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.", +" /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.", +" /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.", +#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 */ +""}; + + +#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, which is providing 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, and 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 that use patterns 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, which is providing 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: 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, and z rather than a through z.", +" ", +#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 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 } 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. If the timeout interval is 0 the", +" MINPUT command does not wait; i.e. the given text must already be", +" available for reading for the MINPUT command to succeed. If the interval", +" is negative, the MINPUT command waits forever.", +" ", +"Also see: INPUT, REINPUT, SET INPUT.", +"" }; +#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:", +" ", +" 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." +, +" ", +" 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\ + Resend the file or files, whose previous transfer was interrupted.\n\ + Picks up from where previous transfer left off, IF the receiver was told\n\ + to SET FILE INCOMPLETE KEEP. Only works for binary-mode transfers.\n\ + Requires the other Kermit to have RESEND capability."; + +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 type", +" ", +" Type of packet block check to be used for error detection, 1, 2, 3, or", +" BLANK-FREE-2. Type 1 is standard, and catches most errors. Types 2 and 3", +" specify more rigorous checking at the cost of higher overhead. The", +" BLANK-FREE-2 type is the same as Type 2, but is guaranteed to contain no", +" blanks.", +"" }; + +static char * hmxydeb[] = { +"Syntax: SET DEBUG { SESSION, ON, OFF, TIMESTAMP }", +" ", +"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.", +"" }; + +#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 succesfully 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.", +" DISCARD - Refuse and/or discard the incoming file.", +" 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 */ +" 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.", +" ", +"Switches:", +" /DEFAULT:text", +" Text to supply if the user enters a blank response or the /TIMEOUT", +" limit expired with no response.", +" ", +#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