diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /gnu/libexec/uucp/cu |
initial import of NetBSD tree
Diffstat (limited to 'gnu/libexec/uucp/cu')
-rw-r--r-- | gnu/libexec/uucp/cu/Makefile | 16 | ||||
-rw-r--r-- | gnu/libexec/uucp/cu/cu.1 | 310 | ||||
-rw-r--r-- | gnu/libexec/uucp/cu/cu.c | 2186 |
3 files changed, 2512 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/cu/Makefile b/gnu/libexec/uucp/cu/Makefile new file mode 100644 index 00000000000..150579b5ebd --- /dev/null +++ b/gnu/libexec/uucp/cu/Makefile @@ -0,0 +1,16 @@ +# Makefile for cu +# $Id: Makefile,v 1.1 1995/10/18 08:38:27 deraadt Exp $ + +BINDIR= $(bindir) +BINOWN= $(owner) +BINMODE= 4555 + +PROG= cu +SRCS= cu.c prot.c log.c chat.c conn.c copy.c +LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP) +CFLAGS+= -I$(.CURDIR)/../common_sources\ + -DVERSION=\"$(VERSION)\" + +.include <bsd.prog.mk> +.PATH: $(.CURDIR)/../common_sources diff --git a/gnu/libexec/uucp/cu/cu.1 b/gnu/libexec/uucp/cu/cu.1 new file mode 100644 index 00000000000..6424ee1d74a --- /dev/null +++ b/gnu/libexec/uucp/cu/cu.1 @@ -0,0 +1,310 @@ +''' $Id: cu.1,v 1.1 1995/10/18 08:38:27 deraadt Exp $ +.TH cu 1 "Taylor UUCP 1.06" +.SH NAME +cu \- Call up another system +.SH SYNOPSIS +.B cu +[ options ] [ system | phone | "dir" ] +.SH DESCRIPTION +The +.I cu +command is used to call up another system and act as a dial in +terminal. It can also do simple file transfers with no error +checking. + +.I cu +takes a single argument, besides the options. If the argument is the +string "dir" cu will make a direct connection to the port. This may +only be used by users with write access to the port, as it permits +reprogramming the modem. + +Otherwise, if the argument begins with a digit, it is taken to be a +phone number to call. Otherwise, it is taken to be the name of a +system to call. The +.B \-z +or +.B \-\-system +option may be used to name a system beginning with a digit, and the +.B \-c +or +.B \-\-phone +option may be used to name a phone number that does not begin with a +digit. + +.I cu +locates a port to use in the UUCP configuration files. If a simple +system name is given, it will select a port appropriate for that +system. The +.B \-p, \-\-port, \-l, \-\-line, \-s +and +.B \-\-speed +options may be used to control the port selection. + +When a connection is made to the remote system, +.I cu +forks into two processes. One reads from the port and writes to the +terminal, while the other reads from the terminal and writes to the +port. + +.I cu +provides several commands that may be used during the conversation. +The commands all begin with an escape character, initially +.B ~ +(tilde). The escape character is only recognized at the beginning of +a line. To send an escape character to the remote system at the start +of a line, it must be entered twice. All commands are either a single +character or a word beginning with +.B % +(percent sign). + +.I cu +recognizes the following commands: + +.TP 5 +.B ~. +Terminate the conversation. +.TP 5 +.B ~! command +Run command in a shell. If command is empty, starts up a shell. +.TP 5 +.B ~$ command +Run command, sending the standard output to the remote system. +.TP 5 +.B ~| command +Run command, taking the standard input from the remote system. +.TP 5 +.B ~+ command +Run command, taking the standard input from the remote system and +sending the standard output to the remote system. +.TP 5 +.B ~#, ~%break +Send a break signal, if possible. +.TP 5 +.B ~c directory, ~%cd directory +Change the local directory. +.TP 5 +.B ~> file +Send a file to the remote system. This just dumps the file over the +communication line. It is assumed that the remote system is expecting +it. +.TP 5 +.B ~< +Receive a file from the remote system. This prompts for the local +file name and for the remote command to execute to begin the file +transfer. It continues accepting data until the contents of the +.B eofread +variable are seen. +.TP 5 +.B ~p from to, ~%put from to +Send a file to a remote Unix system. This runs the appropriate +commands on the remote system. +.TP 5 +.B ~t from to, ~%take from to +Retrieve a file from a remote Unix system. This runs the appropriate +commands on the remote system. +.TP 5 +.B ~s variable value +Set a +.I cu +variable to the given value. If value is not given, the variable is +set to +.B true. +.TP 5 +.B ~! variable +Set a +.I cu +variable to +.B false. +.TP 5 +.B ~z +Suspend the cu session. This is only supported on some systems. On +systems for which ^Z may be used to suspend a job, +.B ~^Z +will also suspend the session. +.TP 5 +.B ~%nostop +Turn off XON/XOFF handling. +.TP 5 +.B ~%stop +Turn on XON/XOFF handling. +.TP 5 +.B ~v +List all the variables and their values. +.TP 5 +.B ~? +List all commands. + +.I cu +also supports several variables. They may be listed with the +.B ~v +command, and set with the +.B ~s +or +.B ~! +commands. + +.TP 5 +.B escape +The escape character. Initially +.B ~ +(tilde). +.TP 5 +.B delay +If this variable is true, +.I cu +will delay for a second after recognizing the escape character before +printing the name of the local system. The default is true. +.TP 5 +.B eol +The list of characters which are considered to finish a line. The +escape character is only recognized after one of these is seen. The +default is carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R. +.TP 5 +.B binary +Whether to transfer binary data when sending a file. If this is +false, then newlines in the file being sent are converted to carriage +returns. The default is false. +.TP 5 +.B binary-prefix +A string used before sending a binary character in a file transfer, if +the +.B binary +variable is true. The default is ^V. +.TP 5 +.B echo-check +Whether to check file transfers by examining what the remote system +echoes back. This probably doesn't work very well. The default is +false. +.TP 5 +.B echonl +The character to look for after sending each line in a file. The +default is carriage return. +.TP 5 +.B timeout +The timeout to use, in seconds, when looking for a character, either +when doing echo checking or when looking for the +.B echonl +character. The default is 30. +.TP 5 +.B kill +The character to use delete a line if the echo check fails. The +default is ^U. +.TP 5 +.B resend +The number of times to resend a line if the echo check continues to +fail. The default is 10. +.TP 5 +.B eofwrite +The string to write after sending a file with the +.B ~> +command. The default is ^D. +.TP 5 +.B eofread +The string to look for when receiving a file with the +.B ~< +command. The default is $, which is intended to be a typical shell +prompt. +.TP 5 +.B verbose +Whether to print accumulated information during a file transfer. The +default is true. +.SH OPTIONS +The following options may be given to +.I cu. +.TP 5 +.B \-e, \-\-parity=even +Use even parity. +.TP 5 +.B \-o, \-\-parity=odd +Use odd parity. +.TP 5 +.B \-\-parity=none +Use no parity. No parity is also used if both +.B \-e +and +.B \-o +are given. +.TP 5 +.B \-h, \-\-halfduplex +Echo characters locally (half-duplex mode). +.TP 5 +.B \-\-nostop +Turn off XON/XOFF handling (it is on by default). +.TP 5 +.B \-E char, \-\-escape char +Set the escape character. Initially +.B ~ +(tilde). To eliminate the escape character, use +.B -E ''. +.TP 5 +.B \-z system, \-\-system system +The system to call. +.TP 5 +.B \-c phone-number, \-\-phone phone-number +The phone number to call. +.TP 5 +.B \-p port, \-\-port port +Name the port to use. +.TP 5 +.B \-a port +Equivalent to +.B \-\-port port. +.TP 5 +.B \-l line, \-\-line line +Name the line to use by giving a device name. This may be used to +dial out on ports that are not listed in the UUCP configuration files. +Write access to the device is required. +.TP 5 +.B \-s speed, \-\-speed speed +The speed (baud rate) to use. +.TP 5 +.B \-# +Where # is a number, equivalent to +.B \-\-speed #. +.TP 5 +.B \-n, \-\-prompt +Prompt for the phone number to use. +.TP 5 +.B \-d +Enter debugging mode. Equivalent to +.B \-\-debug all. +.TP 5 +.B \-x type, \-\-debug type +Turn on particular debugging types. The following types are +recognized: abnormal, chat, handshake, uucp-proto, proto, port, +config, spooldir, execute, incoming, outgoing. Only abnormal, chat, +handshake, port, config, incoming and outgoing are meaningful for +.I cu. + +Multiple types may be given, separated by commas, and the +.B \-\-debug +option may appear multiple times. A number may also be given, which +will turn on that many types from the foregoing list; for example, +.B \-\-debug 2 +is equivalent to +.B \-\-debug abnormal,chat. +.B \-\-debug all +may be used to turn on all debugging options. +.TP 5 +.B \-I file, \-\-config file +Set configuration file to use. This option may not be available, +depending upon how +.I cu +was compiled. +.TP 5 +.B \-v, \-\-version +Report version information and exit. +.TP 5 +.B \-\-help +Print a help message and exit. +.SH BUGS +This program does not work very well. +.SH FILES +The file name may be changed at compilation time, so this is only an +approximation. + +.br +/usr/lib/uucp/config - Configuration file. +.SH AUTHOR +Ian Lance Taylor +<ian@airs.com> diff --git a/gnu/libexec/uucp/cu/cu.c b/gnu/libexec/uucp/cu/cu.c new file mode 100644 index 00000000000..78703f24a44 --- /dev/null +++ b/gnu/libexec/uucp/cu/cu.c @@ -0,0 +1,2186 @@ +/* cu.c + Call up a remote system. + + Copyright (C) 1992, 1993, 1994, 1995 Ian Lance Taylor + + This file is part of the Taylor UUCP package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author of the program may be contacted at ian@airs.com or + c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char cu_rcsid[] = "$Id: cu.c,v 1.1 1995/10/18 08:38:27 deraadt Exp $"; +#endif + +#include "cu.h" +#include "uudefs.h" +#include "uuconf.h" +#include "conn.h" +#include "prot.h" +#include "system.h" +#include "sysdep.h" +#include "getopt.h" + +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +/* Here are the user settable variables. The user is permitted to + change these while running the program, using ~s. */ + +/* The escape character used to introduce a special command. The + escape character is the first character of this string. */ +const char *zCuvar_escape = "~"; + +/* Whether to delay for a second before printing the host name after + seeing an escape character. */ +boolean fCuvar_delay = TRUE; + +/* The input characters which finish a line. The escape character is + only recognized following one of these characters. The default is + carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R, which I got from the + Ultrix /etc/remote file. */ +const char *zCuvar_eol = "\r\025\003\017\004\023\021\022"; + +/* Whether to transfer binary data (nonprintable characters other than + newline and tab) when sending a file. If this is FALSE, then + newline is changed to carriage return. */ +boolean fCuvar_binary = FALSE; + +/* A prefix string to use before sending a binary character from a + file; this is only used if fCuvar_binary is TRUE. The default is + ^V. */ +const char *zCuvar_binary_prefix = "\026"; + +/* Whether to check for echoes of characters sent when sending a file. + This is ignored if fCuvar_binary is TRUE. */ +boolean fCuvar_echocheck = FALSE; + +/* A character to look for after each newline is sent when sending a + file. The character is the first character in this string, except + that a '\0' means that no echo check is done. */ +const char *zCuvar_echonl = "\r"; + +/* The timeout to use when looking for an character. */ +int cCuvar_timeout = 30; + +/* The character to use to kill a line if an echo check fails. The + first character in this string is sent. The default is ^U. */ +const char *zCuvar_kill = "\025"; + +/* The number of times to try resending a line if the echo check keeps + failing. */ +int cCuvar_resend = 10; + +/* The string to send at the end of a file sent with ~>. The default + is ^D. */ +const char *zCuvar_eofwrite = "\004"; + +/* The string to look for to finish a file received with ~<. For tip + this is a collection of single characters, but I don't want to do + that because it means that there are characters which cannot be + received. The default is a guess at a typical shell prompt. */ +const char *zCuvar_eofread = "$"; + +/* Whether to provide verbose information when sending or receiving a + file. */ +boolean fCuvar_verbose = TRUE; + +/* The table used to give a value to a variable, and to print all the + variable values. */ + +static const struct uuconf_cmdtab asCuvars[] = +{ + { "escape", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_escape, NULL }, + { "delay", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_delay, NULL }, + { "eol", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eol, NULL }, + { "binary", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_binary, NULL }, + { "binary-prefix", UUCONF_CMDTABTYPE_STRING, + (pointer) &zCuvar_binary_prefix, NULL }, + { "echocheck", UUCONF_CMDTABTYPE_BOOLEAN, + (pointer) &fCuvar_echocheck, NULL }, + { "echonl", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_echonl, NULL }, + { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_timeout, NULL }, + { "kill", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_kill, NULL }, + { "resend", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_resend, NULL }, + { "eofwrite", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofwrite, NULL }, + { "eofread", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofread, NULL }, + { "verbose", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_verbose, NULL }, + { NULL, 0, NULL, NULL} +}; + +/* The string printed at the initial connect. */ +#if ANSI_C +#define ZCONNMSG "\aConnected." +#else +#define ZCONNMSG "Connected." +#endif + +/* The string printed when disconnecting. */ +#if ANSI_C +#define ZDISMSG "\aDisconnected." +#else +#define ZDISMSG "Disconnected." +#endif + +/* Local variables. */ + +/* The string we print when the user is once again connected to the + port after transferring a file or taking some other action. */ +static const char abCuconnected[] +#if ANSI_C + = "\a[connected]"; +#else + = "[connected]"; +#endif + +/* Global uuconf pointer. */ +static pointer pCuuuconf; + +/* Connection. */ +static struct sconnection *qCuconn; + +/* Whether to close the connection. */ +static boolean fCuclose_conn; + +/* Dialer used to dial out. */ +static struct uuconf_dialer *qCudialer; + +/* Whether we need to restore the terminal. */ +static boolean fCurestore_terminal; + +/* Whether we are doing local echoing. */ +static boolean fCulocalecho; + +/* Whether we need to call fsysdep_cu_finish. */ +static boolean fCustarted; + +/* Whether ZCONNMSG has been printed yet. */ +static boolean fCuconnprinted = FALSE; + +/* A structure used to pass information to icuport_lock. */ +struct sconninfo +{ + boolean fmatched; + boolean flocked; + struct sconnection *qconn; + const char *zline; +}; + +/* Local functions. */ + +static void ucuusage P((void)); +static void ucuhelp P((void)); +static void ucuabort P((void)); +static void uculog_start P((void)); +static void uculog_end P((void)); +static int icuport_lock P((struct uuconf_port *qport, pointer pinfo)); +static boolean fcudo_cmd P((pointer puuconf, struct sconnection *qconn, + int bcmd)); +static boolean fcuset_var P((pointer puuconf, char *zline)); +static int icuunrecogvar P((pointer puuconf, int argc, char **argv, + pointer pvar, pointer pinfo)); +static int icuunrecogfn P((pointer puuconf, int argc, char **argv, + pointer pvar, pointer pinfo)); +static void uculist_vars P((void)); +static void uculist_fns P((const char *zescape)); +static boolean fcudo_subcmd P((pointer puuconf, struct sconnection *qconn, + char *zline)); +static boolean fcusend_buf P((struct sconnection *qconn, const char *zbuf, + size_t cbuf)); + +#define ucuputs(zline) \ + do { if (! fsysdep_terminal_puts (zline)) ucuabort (); } while (0) + +/* Long getopt options. */ +static const struct option asCulongopts[] = +{ + { "phone", required_argument, NULL, 'c' }, + { "escape", required_argument, NULL, 'E' }, + { "parity", required_argument, NULL, 2 }, + { "halfduplex", no_argument, NULL, 'h' }, + { "prompt", no_argument, NULL, 'n' }, + { "line", required_argument, NULL, 'l' }, + { "port", required_argument, NULL, 'p' }, + { "speed", required_argument, NULL, 's' }, + { "baud", required_argument, NULL, 's' }, + { "mapcr", no_argument, NULL, 't' }, + { "nostop", no_argument, NULL, 3 }, + { "system", required_argument, NULL, 'z' }, + { "config", required_argument, NULL, 'I' }, + { "debug", required_argument, NULL, 'x' }, + { "version", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 1 }, + { NULL, 0, NULL, 0 } +}; + +int +main (argc, argv) + int argc; + char **argv; +{ + /* -c: phone number. */ + char *zphone = NULL; + /* -e: even parity. */ + boolean feven = FALSE; + /* -l: line. */ + char *zline = NULL; + /* -n: prompt for phone number. */ + boolean fprompt = FALSE; + /* -o: odd parity. */ + boolean fodd = FALSE; + /* -p: port name. */ + const char *zport = NULL; + /* -s: speed. */ + long ibaud = 0L; + /* -t: map cr to crlf. */ + boolean fmapcr = FALSE; + /* -z: system. */ + const char *zsystem = NULL; + /* --nostop: turn off XON/XOFF. */ + enum txonxoffsetting txonxoff = XONXOFF_ON; + /* -I: configuration file name. */ + const char *zconfig = NULL; + int iopt; + pointer puuconf; + int iuuconf; + const char *zlocalname; + int i; + struct uuconf_system ssys; + const struct uuconf_system *qsys = NULL; + boolean flooped; + struct uuconf_port sport; + struct sconnection sconn; + struct sconninfo sinfo; + long ihighbaud; + struct uuconf_dialer sdialer; + struct uuconf_dialer *qdialer; + char bcmd; + + zProgram = argv[0]; + + /* We want to accept -# as a speed. It's easiest to look through + the arguments, replace -# with -s#, and let getopt handle it. */ + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-' + && isdigit (BUCHAR (argv[i][1]))) + { + size_t clen; + char *z; + + clen = strlen (argv[i]); + z = zbufalc (clen + 2); + z[0] = '-'; + z[1] = 's'; + memcpy (z + 2, argv[i] + 1, clen); + argv[i] = z; + } + } + + while ((iopt = getopt_long (argc, argv, "a:c:deE:hnI:l:op:s:tvx:z:", + asCulongopts, (int *) NULL)) != EOF) + { + switch (iopt) + { + case 'c': + /* Phone number. */ + zphone = optarg; + break; + + case 'd': + /* Set debugging level to maximum. */ +#if DEBUG > 1 + iDebug = DEBUG_MAX; +#endif + break; + + case 'e': + /* Even parity. */ + feven = TRUE; + break; + + case 'E': + /* Escape character. */ + zCuvar_escape = optarg; + break; + + case 'h': + /* Local echo. */ + fCulocalecho = TRUE; + break; + + case 'n': + /* Prompt for phone number. */ + fprompt = TRUE; + break; + + case 'l': + /* Line name. */ + zline = optarg; + break; + + case 'o': + /* Odd parity. */ + fodd = TRUE; + break; + + case 'p': + case 'a': + /* Port name (-a is for compatibility). */ + zport = optarg; + break; + + case 's': + /* Speed. */ + ibaud = strtol (optarg, (char **) NULL, 10); + break; + + case 't': + /* Map cr to crlf. */ + fmapcr = TRUE; + break; + + case 'z': + /* System name. */ + zsystem = optarg; + break; + + case 'I': + /* Configuration file name. */ + if (fsysdep_other_config (optarg)) + zconfig = optarg; + break; + + case 'x': +#if DEBUG > 1 + /* Set debugging level. */ + iDebug |= idebug_parse (optarg); +#endif + break; + + case 'v': + /* Print version and exit. */ + fprintf + (stderr, + "%s: Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", + zProgram, VERSION); + exit (EXIT_SUCCESS); + /*NOTREACHED*/ + + case 2: + /* --parity. */ + if (strncmp (optarg, "even", strlen (optarg)) == 0) + feven = TRUE; + else if (strncmp (optarg, "odd", strlen (optarg)) == 0) + fodd = TRUE; + else if (strncmp (optarg, "none", strlen (optarg)) == 0) + { + feven = TRUE; + fodd = TRUE; + } + else + { + fprintf (stderr, "%s: --parity requires even, odd or none\n", + zProgram); + ucuusage (); + } + break; + + case 3: + /* --nostop. */ + txonxoff = XONXOFF_OFF; + break; + + case 1: + /* --help. */ + ucuhelp (); + exit (EXIT_SUCCESS); + /*NOTREACHED*/ + + case 0: + /* Long option found and flag set. */ + break; + + default: + ucuusage (); + /*NOTREACHED*/ + } + } + + /* There can be one more argument, which is either a system name, a + phone number, or "dir". We decide which it is based on the first + character. To call a UUCP system whose name begins with a digit, + or one which is named "dir", you must use -z. */ + if (optind != argc) + { + if (optind != argc - 1 + || zsystem != NULL + || zphone != NULL) + { + fprintf (stderr, "%s: too many arguments\n", zProgram); + ucuusage (); + } + if (strcmp (argv[optind], "dir") != 0) + { + if (isdigit (BUCHAR (argv[optind][0]))) + zphone = argv[optind]; + else + zsystem = argv[optind]; + } + } + + /* If the user doesn't give a system, port, line or speed, then + there's no basis on which to select a port. */ + if (zsystem == NULL + && zport == NULL + && zline == NULL + && ibaud == 0L) + { + fprintf (stderr, "%s: must specify system, line, port or speed\n", + zProgram); + ucuusage (); + } + + if (fprompt) + { + size_t cphone; + + printf ("Phone number: "); + (void) fflush (stdout); + zphone = NULL; + cphone = 0; + if (getline (&zphone, &cphone, stdin) <= 0 + || *zphone == '\0') + { + fprintf (stderr, "%s: no phone number entered\n", zProgram); + exit (EXIT_FAILURE); + } + } + + iuuconf = uuconf_init (&puuconf, "cu", zconfig); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + pCuuuconf = puuconf; + +#if DEBUG > 1 + { + const char *zdebug; + + iuuconf = uuconf_debuglevel (puuconf, &zdebug); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + if (zdebug != NULL) + iDebug |= idebug_parse (zdebug); + } +#endif + + usysdep_initialize (puuconf, INIT_NOCHDIR | INIT_SUID); + + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + exit (EXIT_FAILURE); + } + else if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + + ulog_fatal_fn (ucuabort); + pfLstart = uculog_start; + pfLend = uculog_end; + +#ifdef SIGINT + usysdep_signal (SIGINT); +#endif +#ifdef SIGHUP + usysdep_signal (SIGHUP); +#endif +#ifdef SIGQUIT + usysdep_signal (SIGQUIT); +#endif +#ifdef SIGTERM + usysdep_signal (SIGTERM); +#endif +#ifdef SIGPIPE + usysdep_signal (SIGPIPE); +#endif + + if (zsystem != NULL) + { + iuuconf = uuconf_system_info (puuconf, zsystem, &ssys); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + ulog (LOG_FATAL, "%s: System not found", zsystem); + } + qsys = &ssys; + } + + /* This loop is used if a system is specified. It loops over the + various alternates until it finds one for which the dial + succeeds. This is an ugly spaghetti construction, and it should + be broken up into different functions someday. */ + flooped = FALSE; + while (TRUE) + { + enum tparitysetting tparity; + enum tstripsetting tstrip; + long iusebaud; + + /* The uuconf_find_port function only selects directly on a port + name and a speed. To select based on the line name, we use a + function. If we can't find any defined port, and the user + specified a line name but did not specify a port name or a + system or a phone number, then we fake a direct port with + that line name (we don't fake a port if a system or phone + number were given because if we fake a port we have no way to + place a call; perhaps we should automatically look up a + particular dialer). This permits users to say cu -lttyd0 + without having to put ttyd0 in the ports file, provided they + have read and write access to the port. */ + sinfo.fmatched = FALSE; + sinfo.flocked = FALSE; + sinfo.qconn = &sconn; + sinfo.zline = zline; + if (zport != NULL || zline != NULL || ibaud != 0L) + { + iuuconf = uuconf_find_port (puuconf, zport, ibaud, 0L, + icuport_lock, (pointer) &sinfo, + &sport); + if (iuuconf != UUCONF_SUCCESS) + { + if (iuuconf != UUCONF_NOT_FOUND) + { + if (sinfo.flocked) + { + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + } + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + if (zline == NULL + || zport != NULL + || zphone != NULL + || qsys != NULL) + { + if (sinfo.fmatched) + ulog (LOG_FATAL, "All matching ports in use"); + else + ulog (LOG_FATAL, "No matching ports"); + } + + sport.uuconf_zname = zline; + sport.uuconf_ttype = UUCONF_PORTTYPE_DIRECT; + sport.uuconf_zprotocols = NULL; + sport.uuconf_qproto_params = NULL; + sport.uuconf_ireliable = 0; + sport.uuconf_zlockname = NULL; + sport.uuconf_palloc = NULL; + sport.uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL; + sport.uuconf_u.uuconf_sdirect.uuconf_ibaud = ibaud; + + if (! fconn_init (&sport, &sconn, UUCONF_PORTTYPE_UNKNOWN)) + ucuabort (); + + if (! fconn_lock (&sconn, FALSE)) + ulog (LOG_FATAL, "%s: Line in use", zline); + + qCuconn = &sconn; + + /* Check user access after locking the port, because on + some systems shared lines affect the ownership and + permissions. In such a case ``Line in use'' is more + clear than ``Permission denied.'' */ + if (! fsysdep_port_access (&sport)) + ulog (LOG_FATAL, "%s: Permission denied", zline); + } + iusebaud = ibaud; + ihighbaud = 0L; + } + else + { + for (; qsys != NULL; qsys = qsys->uuconf_qalternate) + { + if (! qsys->uuconf_fcall) + continue; + if (qsys->uuconf_qport != NULL) + { + if (fconn_init (qsys->uuconf_qport, &sconn, + UUCONF_PORTTYPE_UNKNOWN)) + { + if (fconn_lock (&sconn, FALSE)) + { + qCuconn = &sconn; + break; + } + uconn_free (&sconn); + } + } + else + { + sinfo.fmatched = FALSE; + sinfo.flocked = FALSE; + sinfo.qconn = &sconn; + iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport, + qsys->uuconf_ibaud, + qsys->uuconf_ihighbaud, + icuport_lock, + (pointer) &sinfo, + &sport); + if (iuuconf == UUCONF_SUCCESS) + break; + if (iuuconf != UUCONF_NOT_FOUND) + { + if (sinfo.flocked) + { + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + } + ulog_uuconf (LOG_FATAL, puuconf, iuuconf); + } + } + } + + if (qsys == NULL) + { + const char *zrem; + + if (flooped) + zrem = "remaining "; + else + zrem = ""; + if (sinfo.fmatched) + ulog (LOG_FATAL, "%s: All %smatching ports in use", + zsystem, zrem); + else + ulog (LOG_FATAL, "%s: No %smatching ports", zsystem, zrem); + } + + iusebaud = qsys->uuconf_ibaud; + ihighbaud = qsys->uuconf_ihighbaud; + } + + /* Here we have locked a connection to use. */ + if (! fconn_open (&sconn, iusebaud, ihighbaud, FALSE)) + ucuabort (); + + fCuclose_conn = TRUE; + + if (FGOT_SIGNAL ()) + ucuabort (); + + /* Set up the connection. */ + if (fodd && feven) + { + tparity = PARITYSETTING_NONE; + tstrip = STRIPSETTING_SEVENBITS; + } + else if (fodd) + { + tparity = PARITYSETTING_ODD; + tstrip = STRIPSETTING_SEVENBITS; + } + else if (feven) + { + tparity = PARITYSETTING_EVEN; + tstrip = STRIPSETTING_SEVENBITS; + } + else + { + tparity = PARITYSETTING_DEFAULT; + tstrip = STRIPSETTING_DEFAULT; + } + + if (! fconn_set (&sconn, tparity, tstrip, txonxoff)) + ucuabort (); + + if (qsys != NULL) + zphone = qsys->uuconf_zphone; + + if (qsys != NULL || zphone != NULL) + { + enum tdialerfound tdialer; + + if (! fconn_dial (&sconn, puuconf, qsys, zphone, &sdialer, + &tdialer)) + { + if (zport != NULL + || zline != NULL + || ibaud != 0L + || qsys == NULL) + ucuabort (); + + qsys = qsys->uuconf_qalternate; + if (qsys == NULL) + ulog (LOG_FATAL, "%s: No remaining alternates", zsystem); + + fCuclose_conn = FALSE; + (void) fconn_close (&sconn, pCuuuconf, qCudialer, FALSE); + qCuconn = NULL; + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + + /* Loop around and try another alternate. */ + flooped = TRUE; + continue; + } + if (tdialer == DIALERFOUND_FALSE) + qdialer = NULL; + else + qdialer = &sdialer; + } + else + { + /* If no system or phone number was specified, we connect + directly to the modem. We only permit this if the user + has access to the port, since it permits various + shenanigans such as reprogramming the automatic + callbacks. */ + if (! fsysdep_port_access (sconn.qport)) + ulog (LOG_FATAL, "Access to port denied"); + qdialer = NULL; + if (! fconn_carrier (&sconn, FALSE)) + ulog (LOG_FATAL, "Can't turn off carrier"); + } + + break; + } + + qCudialer = qdialer; + + if (FGOT_SIGNAL ()) + ucuabort (); + + /* Here we have connected, and can start the main cu protocol. The + program spends most of its time in system dependent code, and + only comes out when a special command is received from the + terminal. */ + printf ("%s\n", ZCONNMSG); + fCuconnprinted = TRUE; + + if (! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + + fCurestore_terminal = TRUE; + + if (! fsysdep_cu_init (&sconn)) + ucuabort (); + + fCustarted = TRUE; + + while (fsysdep_cu (&sconn, &bcmd, zlocalname)) + if (! fcudo_cmd (puuconf, &sconn, bcmd)) + break; + + fCustarted = FALSE; + if (! fsysdep_cu_finish ()) + ucuabort (); + + fCurestore_terminal = FALSE; + (void) fsysdep_terminal_restore (); + + (void) fconn_close (&sconn, puuconf, qdialer, TRUE); + (void) fconn_unlock (&sconn); + uconn_free (&sconn); + + if (fCuconnprinted) + printf ("\n%s\n", ZDISMSG); + + ulog_close (); + + usysdep_exit (TRUE); + + /* Avoid errors about not returning a value. */ + return 0; +} + +/* Print a usage message and die. */ + +static void +ucuusage () +{ + fprintf (stderr, "Usage: %s [options] [system or phone-number]\n", + zProgram); + fprintf (stderr, "Use %s --help for help\n", zProgram); + exit (EXIT_FAILURE); +} + +/* Print a help message. */ + +static void +ucuhelp () +{ + fprintf (stderr, + "Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", + VERSION); + fprintf (stderr, + "Usage: %s [options] [system or phone-number]\n", zProgram); + fprintf (stderr, + " -a,-p,--port port: Use named port\n"); + fprintf (stderr, + " -l,--line line: Use named device (e.g. tty0)\n"); + fprintf (stderr, + " -s,--speed,--baud speed, -#: Use given speed\n"); + fprintf (stderr, + " -c,--phone phone: Phone number to call\n"); + fprintf (stderr, + " -z,--system system: System to call\n"); + fprintf (stderr, + " -e: Set even parity\n"); + fprintf (stderr, + " -o: Set odd parity\n"); + fprintf (stderr, + " --parity={odd,even}: Set parity\n"); + fprintf (stderr, + " -E,--escape char: Set escape character\n"); + fprintf (stderr, + " -h,--halfduplex: Echo locally\n"); + fprintf (stderr, + " --nostop: Turn off XON/XOFF handling\n"); + fprintf (stderr, + " -t,--mapcr: Map carriage return to carriage return/linefeed\n"); + fprintf (stderr, + " -n,--prompt: Prompt for phone number\n"); + fprintf (stderr, + " -d: Set maximum debugging level\n"); + fprintf (stderr, + " -x,--debug debug: Set debugging type\n"); +#if HAVE_TAYLOR_CONFIG + fprintf (stderr, + " -I,--config file: Set configuration file to use\n"); +#endif /* HAVE_TAYLOR_CONFIG */ + fprintf (stderr, + " -v,--version: Print version and exit\n"); + fprintf (stderr, + " --help: Print help and exit\n"); +} + +/* This function is called when a fatal error occurs. */ + +static void +ucuabort () +{ + if (fCustarted) + { + fCustarted = FALSE; + (void) fsysdep_cu_finish (); + } + + if (fCurestore_terminal) + { + fCurestore_terminal = FALSE; + (void) fsysdep_terminal_restore (); + } + + if (qCuconn != NULL) + { + struct sconnection *qconn; + + if (fCuclose_conn) + { + fCuclose_conn = FALSE; + (void) fconn_close (qCuconn, pCuuuconf, qCudialer, FALSE); + } + qconn = qCuconn; + qCuconn = NULL; + (void) fconn_unlock (qconn); + uconn_free (qconn); + } + + ulog_close (); + + if (fCuconnprinted) + printf ("\n%s\n", ZDISMSG); + + usysdep_exit (FALSE); +} + +/* This variable is just used to communicate between uculog_start and + uculog_end. */ +static boolean fCulog_restore; + +/* This function is called by ulog before it output anything. We use + it to restore the terminal, if necessary. ulog is only called for + errors or debugging in cu, so it's not too costly to do this. If + we didn't do it, then at least on Unix each line would leave the + cursor in the same column rather than wrapping back to the start, + since CRMOD will not be on. */ + +static void +uculog_start () +{ + if (! fCurestore_terminal) + fCulog_restore = FALSE; + else + { + fCulog_restore = TRUE; + fCurestore_terminal = FALSE; + if (! fsysdep_terminal_restore ()) + ucuabort (); + } +} + +/* This function is called by ulog after everything is output. It + sets the terminal back, if necessary. */ + +static void +uculog_end () +{ + if (fCulog_restore) + { + if (! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + } +} + +/* Check to see if this port has the desired line, to handle the -l + option. If it does, or if no line was specified, set up a + connection and lock it. */ + +static int +icuport_lock (qport, pinfo) + struct uuconf_port *qport; + pointer pinfo; +{ + struct sconninfo *q = (struct sconninfo *) pinfo; + + if (q->zline != NULL + && ! fsysdep_port_is_line (qport, q->zline)) + return UUCONF_NOT_FOUND; + + q->fmatched = TRUE; + + if (! fconn_init (qport, q->qconn, UUCONF_PORTTYPE_UNKNOWN)) + return UUCONF_NOT_FOUND; + else if (! fconn_lock (q->qconn, FALSE)) + { + uconn_free (q->qconn); + return UUCONF_NOT_FOUND; + } + else + { + qCuconn = q->qconn; + q->flocked = TRUE; + return UUCONF_SUCCESS; + } +} + +/* Execute a cu escape command. Return TRUE if the connection should + continue, or FALSE if the connection should be terminated. */ + +static boolean +fcudo_cmd (puuconf, qconn, bcmd) + pointer puuconf; + struct sconnection *qconn; + int bcmd; +{ + char *zline; + char *z; + char abescape[5]; + boolean fret; + size_t clen; + char abbuf[100]; + + /* Some commands take a string up to the next newline character. */ + switch (bcmd) + { + default: + zline = NULL; + break; + case '!': + case '$': + case '|': + case '+': + case '%': + case 'c': + case '>': + case '<': + case 'p': + case 't': + case 's': + { + zline = zsysdep_terminal_line ((const char *) NULL); + if (zline == NULL) + ucuabort (); + zline[strcspn (zline, "\n")] = '\0'; + } + break; + } + + switch (bcmd) + { + default: + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + sprintf (abbuf, "[Unrecognized. Use %s%s to send %s]", + abescape, abescape, abescape); + ucuputs (abbuf); + return TRUE; + + case '.': + /* Hangup. */ + return FALSE; + + case '!': + case '$': + case '|': + case '+': + /* Shell out. */ + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_restore ()) + ucuabort (); + fCurestore_terminal = FALSE; + { + enum tshell_cmd t; + + switch (bcmd) + { + default: + case '!': t = SHELL_NORMAL; break; + case '$': t = SHELL_STDOUT_TO_PORT; break; + case '|': t = SHELL_STDIN_FROM_PORT; break; + case '+': t = SHELL_STDIO_ON_PORT; break; + } + + (void) fsysdep_shell (qconn, zline, t); + } + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + ubuffree (zline); + return TRUE; + + case '%': + fret = fcudo_subcmd (puuconf, qconn, zline); + ubuffree (zline); + return fret; + + case '#': + if (! fconn_break (qconn)) + ucuabort (); + return TRUE; + + case 'c': + (void) fsysdep_chdir (zline); + ubuffree (zline); + return TRUE; + + case '>': + case '<': + case 'p': + case 't': + clen = strlen (zline); + z = zbufalc (clen + 3); + z[0] = bcmd; + z[1] = ' '; + memcpy (z + 2, zline, clen + 1); + ubuffree (zline); + fret = fcudo_subcmd (puuconf, qconn, z); + ubuffree (z); + return fret; + + case 'z': + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_restore ()) + ucuabort (); + fCurestore_terminal = FALSE; + if (! fsysdep_suspend ()) + ucuabort (); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_raw (fCulocalecho)) + ucuabort (); + fCurestore_terminal = TRUE; + return TRUE; + + case 's': + fret = fcuset_var (puuconf, zline); + ubuffree (zline); + return fret; + + case 'v': + uculist_vars (); + return TRUE; + + case '?': + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + ucuputs (""); + ucuputs ("[Escape sequences]"); + sprintf (abbuf, + "[%s. hangup] [%s!CMD run shell]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s$CMD stdout to remote] [%s|CMD stdin from remote]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s+CMD stdin and stdout to remote]", + abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s# send break] [%scDIR change directory]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s> send file] [%s< receive file]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%spFROM TO send to Unix] [%stFROM TO receive from Unix]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%ssVAR VAL set variable] [%ssVAR set boolean]", + abescape, abescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%ss!VAR unset boolean] [%sv list variables]", + abescape, abescape); + ucuputs (abbuf); +#ifdef SIGTSTP + sprintf (abbuf, + "[%sz suspend]", + abescape); + ucuputs (abbuf); +#endif + uculist_fns (abescape); + return TRUE; + } +} + +/* List ~% functions. */ + +static void +uculist_fns (zescape) + const char *zescape; +{ + char abbuf[100]; + + sprintf (abbuf, + "[%s%%break send break] [%s%%cd DIR change directory]", + zescape, zescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s%%put FROM TO send file] [%s%%take FROM TO receive file]", + zescape, zescape); + ucuputs (abbuf); + sprintf (abbuf, + "[%s%%nostop no XON/XOFF] [%s%%stop use XON/XOFF]", + zescape, zescape); + ucuputs (abbuf); +} + +/* Set a variable. */ + +static boolean +fcuset_var (puuconf, zline) + pointer puuconf; + char *zline; +{ + char *zvar, *zval; + char *azargs[2]; + int iuuconf; + + zvar = strtok (zline, "= \t"); + if (zvar == NULL) + { + ucuputs (abCuconnected); + return TRUE; + } + + zval = strtok ((char *) NULL, " \t"); + + if (zval == NULL) + { + azargs[0] = zvar; + if (azargs[0][0] != '!') + azargs[1] = zbufcpy ("t"); + else + { + ++azargs[0]; + azargs[1] = zbufcpy ("f"); + } + } + else + { + azargs[0] = zvar; + azargs[1] = zbufcpy (zval); + } + + iuuconf = uuconf_cmd_args (puuconf, 2, azargs, asCuvars, + (pointer) NULL, icuunrecogvar, 0, + (pointer) NULL); + + if ((iuuconf & UUCONF_CMDTABRET_KEEP) == 0) + ubuffree (azargs[1]); + + if ((iuuconf &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS) + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + + return TRUE; +} + +/* Warn about an unknown variable. */ + +/*ARGSUSED*/ +static int +icuunrecogvar (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char abescape[5]; + + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + ulog (LOG_ERROR, "%s: unknown variable (%sv lists variables)", + argv[0], abescape); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* List all the variables with their values. */ + +static void +uculist_vars () +{ + const struct uuconf_cmdtab *q; + char abbuf[100]; + + ucuputs (""); + for (q = asCuvars; q->uuconf_zcmd != NULL; q++) + { + switch (UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype)) + { + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN): + if (*(boolean *) q->uuconf_pvar) + sprintf (abbuf, "%s true", q->uuconf_zcmd); + else + sprintf (abbuf, "%s false", q->uuconf_zcmd); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT): + sprintf (abbuf, "%s %d", q->uuconf_zcmd, *(int *) q->uuconf_pvar); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG): + sprintf (abbuf, "%s %ld", q->uuconf_zcmd, + *(long *) q->uuconf_pvar); + break; + + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING): + case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING): + { + const char *z; + char abchar[5]; + size_t clen; + + sprintf (abbuf, "%s ", q->uuconf_zcmd); + clen = strlen (abbuf); + for (z = *(const char **) q->uuconf_pvar; *z != '\0'; z++) + { + int cchar; + + if (! isprint (*z)) + { + sprintf (abchar, "\\%03o", BUCHAR (*z)); + cchar = 4; + } + else + { + abchar[0] = *z; + abchar[1] = '\0'; + cchar = 1; + } + if (clen + cchar < sizeof (abbuf)) + strcat (abbuf, abchar); + clen += cchar; + } + } + break; + + default: + sprintf (abbuf, "%s [unprintable type]", q->uuconf_zcmd); + break; + } + + ucuputs (abbuf); + } +} + +/* Subcommands. These are commands that begin with ~%. */ + +/* This variable is only used so that we can pass a non-NULL address + in pvar. It is never assigned to or examined. */ + +static char bCutype; + +/* The command table for the subcommands. */ + +static int icubreak P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icudebug P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icuchdir P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icuput P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icutake P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); +static int icunostop P((pointer puuconf, int argc, char **argv, pointer pvar, + pointer pinfo)); + +static const struct uuconf_cmdtab asCucmds[] = +{ + { "break", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, + { "b", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak }, + { "cd", UUCONF_CMDTABTYPE_FN | 0, NULL, icuchdir }, + { "d", UUCONF_CMDTABTYPE_FN | 1, NULL, icudebug }, + { "put", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, + { "take", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, + { "nostop", UUCONF_CMDTABTYPE_FN | 1, NULL, icunostop }, + { "stop", UUCONF_CMDTABTYPE_FN | 1, &bCutype, icunostop }, + { ">", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icuput }, + { "<", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icutake }, + { "p", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput }, + { "t", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake }, + { NULL, 0, NULL, NULL } +}; + +/* Do a subcommand. This is called by commands beginning with ~%. */ + +static boolean +fcudo_subcmd (puuconf, qconn, zline) + pointer puuconf; + struct sconnection *qconn; + char *zline; +{ + char *azargs[3]; + int iarg; + int iuuconf; + + for (iarg = 0; iarg < 3; iarg++) + { + azargs[iarg] = strtok (iarg == 0 ? zline : (char *) NULL, " \t\n"); + if (azargs[iarg] == NULL) + break; + } + + if (iarg == 0) + { + ucuputs (abCuconnected); + return TRUE; + } + + iuuconf = uuconf_cmd_args (puuconf, iarg, azargs, asCucmds, + (pointer) qconn, icuunrecogfn, + 0, (pointer) NULL); + if (iuuconf != UUCONF_SUCCESS) + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + + return TRUE; +} + +/* Warn about an unknown function. */ + +/*ARGSUSED*/ +static int +icuunrecogfn (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + char abescape[5]; + + if (! isprint (*zCuvar_escape)) + sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape)); + else + { + abescape[0] = *zCuvar_escape; + abescape[1] = '\0'; + } + if (argv[0][0] == '?') + uculist_fns (abescape); + else + ulog (LOG_ERROR, "%s: unknown (%s%%? lists choices)", + argv[0], abescape); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a break. */ + +/*ARGSUSED*/ +static int +icubreak (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + + if (! fconn_break (qconn)) + ucuabort (); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Change directories. */ + +/*ARGSUSED*/ +static int +icuchdir (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + const char *zarg; + + if (argc <= 1) + zarg = NULL; + else + zarg = argv[1]; + (void) fsysdep_chdir (zarg); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Toggle debugging. */ + +/*ARGSUSED*/ +static int +icudebug (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ +#if DEBUG > 1 + if (iDebug != 0) + iDebug = 0; + else + iDebug = DEBUG_MAX; +#else + ucuputs ("[compiled without debugging]"); +#endif + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Control whether the port does xon/xoff handshaking. If pvar is not + NULL, this is "stop"; otherwise it is "nostop". */ + +/*ARGSUSED*/ +static int +icunostop (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + + if (! fconn_set (qconn, PARITYSETTING_DEFAULT, STRIPSETTING_DEFAULT, + pvar == NULL ? XONXOFF_OFF : XONXOFF_ON)) + ucuabort (); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a file to the remote system. The first argument is the file + to send. If that argument is not present, it is prompted for. The + second argument is to file name to use on the remote system. If + that argument is not present, the basename of the local filename is + used. If pvar is not NULL, then this is ~>, which is used to send + a command to a non-Unix system. We treat is the same as ~%put, + except that we assume the user has already entered the appropriate + command (for ~%put, we force ``cat >to'' to the other side). */ + +/*ARGSUSED*/ +static int +icuput (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + char *zfrom; + char *zto = NULL; + char *zalc; + openfile_t e; + int cline; + char *zbuf; + size_t cbuf; + + if (argc > 1) + zfrom = zbufcpy (argv[1]); + else + { + zfrom = zsysdep_terminal_line ("File to send: "); + if (zfrom == NULL) + ucuabort (); + zfrom[strcspn (zfrom, " \t\n")] = '\0'; + + if (*zfrom == '\0') + { + ubuffree (zfrom); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + if (pvar == NULL) + { + if (argc > 2) + zto = zbufcpy (argv[2]); + else + { + char *zbase; + char *zprompt; + + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + ucuabort (); + + zprompt = zbufalc (sizeof "Remote file name []: " + + strlen (zbase)); + sprintf (zprompt, "Remote file name [%s]: ", zbase); + zto = zsysdep_terminal_line (zprompt); + ubuffree (zprompt); + if (zto == NULL) + ucuabort (); + + zto[strcspn (zto, " \t\n")] = '\0'; + if (*zto != '\0') + ubuffree (zbase); + else + { + ubuffree (zto); + zto = zbase; + } + } + } + + e = esysdep_user_fopen (zfrom, TRUE, fCuvar_binary); + if (! ffileisopen (e)) + { + const char *zerrstr; + + if (pvar == NULL) + ubuffree (zto); + zerrstr = strerror (errno); + zalc = zbufalc (strlen (zfrom) + sizeof ": " + strlen (zerrstr)); + sprintf (zalc, "%s: %s", zfrom, zerrstr); + ubuffree (zfrom); + ucuputs (zalc); + ubuffree (zalc); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + + ubuffree (zfrom); + + /* Tell the system dependent layer to stop copying data from the + port to the terminal. We want to read the echoes ourself. Also + permit the local user to generate signals. */ + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_signals (TRUE)) + ucuabort (); + + /* If pvar is NULL, then we are sending a file to a Unix system. We + send over the command "cat > TO" to prepare it to receive. If + pvar is not NULL, the user is assumed to have set up whatever + action was needed to receive the file. */ + if (pvar == NULL) + { + boolean fret; + + zalc = zbufalc (sizeof "cat > \n" + strlen (zto)); + sprintf (zalc, "cat > %s\n", zto); + ubuffree (zto); + fret = fcusend_buf (qconn, zalc, strlen (zalc)); + ubuffree (zalc); + if (! fret) + { + (void) ffileclose (e); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + cline = 0; + + zbuf = NULL; + cbuf = 0; + + while (TRUE) + { + char abbuf[512]; + size_t c; + +#if USE_STDIO + if (fCuvar_binary) +#endif + { + if (ffileeof (e)) + break; + c = cfileread (e, abbuf, sizeof abbuf); + if (ffileioerror (e, c)) + { + ucuputs ("[file read error]"); + break; + } + if (c == 0) + break; + zbuf = abbuf; + } +#if USE_STDIO + else + { + if (getline (&zbuf, &cbuf, e) <= 0) + { + xfree ((pointer) zbuf); + break; + } + c = strlen (zbuf); + } +#endif + + if (fCuvar_verbose) + { + ++cline; + printf ("%d ", cline); + (void) fflush (stdout); + } + + if (! fcusend_buf (qconn, zbuf, c)) + { + if (! fCuvar_binary) + xfree ((pointer) zbuf); + (void) fclose (e); + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + (void) ffileclose (e); + + if (pvar == NULL) + { + char beof; + + beof = '\004'; + if (! fconn_write (qconn, &beof, 1)) + ucuabort (); + } + else + { + if (*zCuvar_eofwrite != '\0') + { + if (! fconn_write (qconn, zCuvar_eofwrite, + strlen (zCuvar_eofwrite))) + ucuabort (); + } + } + + if (fCuvar_verbose) + ucuputs (""); + + ucuputs ("[file transfer complete]"); + + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Get a file from the remote side. This is ~%take, or ~t, or ~<. + The first two are assumed to be taking the file from a Unix system, + so we force the command "cat FROM; echo */ + +/*ARGSUSED*/ +static int +icutake (puuconf, argc, argv, pvar, pinfo) + pointer puuconf; + int argc; + char **argv; + pointer pvar; + pointer pinfo; +{ + struct sconnection *qconn = (struct sconnection *) pinfo; + const char *zeof; + char *zfrom, *zto, *zcmd; + char *zalc; + openfile_t e; + char bcr; + size_t ceoflen; + char *zlook = NULL; + size_t ceofhave; + boolean ferr; + + if (argc > 1) + zfrom = zbufcpy (argv[1]); + else + { + zfrom = zsysdep_terminal_line ("Remote file to retreive: "); + if (zfrom == NULL) + ucuabort (); + zfrom[strcspn (zfrom, " \t\n")] = '\0'; + if (*zfrom == '\0') + { + ubuffree (zfrom); + ucuputs (abCuconnected); + return UUCONF_CMDTABRET_CONTINUE; + } + } + + if (argc > 2) + zto = zbufcpy (argv[2]); + else + { + char *zbase; + char *zprompt; + + zbase = zsysdep_base_name (zfrom); + if (zbase == NULL) + ucuabort (); + + zprompt = zbufalc (sizeof "Local file name []: " + strlen (zbase)); + sprintf (zprompt, "Local file name [%s]: ", zbase); + zto = zsysdep_terminal_line (zprompt); + ubuffree (zprompt); + if (zto == NULL) + ucuabort (); + + zto[strcspn (zto, " \t\n")] = '\0'; + if (*zto != '\0') + ubuffree (zbase); + else + { + ubuffree (zto); + zto = zbase; + } + } + + if (pvar != NULL) + { + zcmd = zsysdep_terminal_line ("Remote command to execute: "); + if (zcmd == NULL) + ucuabort (); + zcmd[strcspn (zcmd, "\n")] = '\0'; + zeof = zCuvar_eofread; + } + else + { + zcmd = zbufalc (sizeof "cat ; echo; echo ////cuend////" + + strlen (zfrom)); + sprintf (zcmd, "cat %s; echo; echo ////cuend////", zfrom); + zeof = "\n////cuend////\n"; + } + + ubuffree (zfrom); + + e = esysdep_user_fopen (zto, FALSE, fCuvar_binary); + if (! ffileisopen (e)) + { + const char *zerrstr; + + ubuffree (zcmd); + zerrstr = strerror (errno); + zalc = zbufalc (strlen (zto) + sizeof ": " + strlen (zerrstr)); + sprintf (zalc, "%s: %s\n", zto, zerrstr); + ucuputs (zalc); + ubuffree (zalc); + ucuputs (abCuconnected); + ubuffree (zto); + return UUCONF_CMDTABRET_CONTINUE; + } + + if (! fsysdep_cu_copy (FALSE) + || ! fsysdep_terminal_signals (TRUE)) + ucuabort (); + + if (! fconn_write (qconn, zcmd, strlen (zcmd))) + ucuabort (); + bcr = '\r'; + if (! fconn_write (qconn, &bcr, 1)) + ucuabort (); + + ubuffree (zcmd); + + /* Eliminated any previously echoed data to avoid confusion. */ + iPrecstart = 0; + iPrecend = 0; + + /* If we're dealing with a Unix system, we can reliably discard the + command. Otherwise, the command will probably wind up in the + file; too bad. */ + if (pvar == NULL) + { + int b; + + while ((b = breceive_char (qconn, cCuvar_timeout, TRUE)) != '\n') + { + if (b == -2) + ucuabort (); + if (b < 0) + { + ucuputs ("[timed out waiting for newline]"); + ucuputs (abCuconnected); + ubuffree (zto); + return UUCONF_CMDTABRET_CONTINUE; + } + } + } + + ceoflen = strlen (zeof); + zlook = zbufalc (ceoflen); + ceofhave = 0; + ferr = FALSE; + + while (TRUE) + { + int b; + + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file receive aborted]"); + /* Reset the SIGINT flag so that it does not confuse us in + the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + break; + } + + b = breceive_char (qconn, cCuvar_timeout, TRUE); + if (b == -2) + ucuabort (); + if (b < 0) + { + if (ceofhave > 0) + (void) fwrite (zlook, sizeof (char), ceofhave, e); + ucuputs ("[timed out]"); + break; + } + + if (b == '\r' && ! fCuvar_binary) + continue; + + if (ceoflen == 0) + { + if (cfilewrite (e, &b, 1) != 1) + { + ferr = TRUE; + break; + } + } + else + { + zlook[ceofhave] = b; + ++ceofhave; + if (ceofhave == ceoflen) + { + size_t cmove; + char *zmove; + + if (memcmp (zeof, zlook, ceoflen) == 0) + { + ucuputs ("[file transfer complete]"); + break; + } + + if (cfilewrite (e, zlook, 1) != 1) + { + ferr = TRUE; + break; + } + + zmove = zlook; + for (cmove = ceoflen - 1, zmove = zlook; + cmove > 0; + cmove--, zmove++) + zmove[0] = zmove[1]; + + --ceofhave; + } + } + } + + ubuffree (zlook); + + if (! fsysdep_sync (e, zto)) + { + (void) ffileclose (e); + ferr = TRUE; + } + else + { + if (! ffileclose (e)) + ferr = TRUE; + } + if (ferr) + ucuputs ("[file write error]"); + + if (! fsysdep_cu_copy (TRUE) + || ! fsysdep_terminal_signals (FALSE)) + ucuabort (); + + ucuputs (abCuconnected); + + ubuffree (zto); + + return UUCONF_CMDTABRET_CONTINUE; +} + +/* Send a buffer to the remote system. If fCuvar_binary is FALSE, + each buffer passed in will be a single line; in this case we can + check the echoed characters and kill the line if they do not match. + This returns FALSE if an echo check fails. If a port error + occurrs, it calls ucuabort. */ + +static boolean +fcusend_buf (qconn, zbufarg, cbufarg) + struct sconnection *qconn; + const char *zbufarg; + size_t cbufarg; +{ + const char *zbuf; + size_t cbuf; + int ctries; + size_t cbplen; + char *zsendbuf; + + zbuf = zbufarg; + cbuf = cbufarg; + ctries = 0; + + if (fCuvar_binary) + cbplen = strlen (zCuvar_binary_prefix); + else + cbplen = 1; + zsendbuf = zbufalc (64 * (cbplen + 1)); + + /* Loop while we still have characters to send. The value of cbuf + will be reset to cbufarg if an echo failure occurs while sending + a line in non-binary mode. */ + while (cbuf > 0) + { + int csend; + char *zput; + const char *zget; + boolean fnl; + int i; + + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ubuffree (zsendbuf); + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file send aborted]"); + /* Reset the SIGINT flag so that it does not confuse us in + the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + return FALSE; + } + + /* Discard anything we've read from the port up to now, to avoid + confusing the echo checking. */ + iPrecstart = 0; + iPrecend = 0; + + /* Send all characters up to a newline before actually sending + the newline. This makes it easier to handle the special + newline echo checking. Send up to 64 characters at a time + before doing echo checking. */ + if (*zbuf == '\n') + csend = 1; + else + { + const char *znl; + + znl = memchr (zbuf, '\n', cbuf); + if (znl == NULL) + csend = cbuf; + else + csend = znl - zbuf; + if (csend > 64) + csend = 64; + } + + /* Translate this part of the buffer. If we are not in binary + mode, we translate \n to \r, and ignore any nonprintable + characters. */ + zput = zsendbuf; + fnl = FALSE; + for (i = 0, zget = zbuf; i < csend; i++, zget++) + { + if (isprint (*zget) + || *zget == '\t') + *zput++ = *zget; + else if (*zget == '\n') + { + if (fCuvar_binary) + *zput++ = '\n'; + else + *zput++ = '\r'; + fnl = TRUE; + } + else if (fCuvar_binary) + { + strcpy (zput, zCuvar_binary_prefix); + zput += cbplen; + *zput++ = *zget; + } + } + + zbuf += csend; + cbuf -= csend; + + if (zput == zsendbuf) + continue; + + /* Send the data over the port. */ + if (! fsend_data (qconn, zsendbuf, (size_t) (zput - zsendbuf), TRUE)) + ucuabort (); + + /* We do echo checking if requested, unless we are in binary + mode. Echo checking of a newline is different from checking + of normal characters; when we send a newline we look for + *zCuvar_echonl. */ + if ((fCuvar_echocheck && ! fCuvar_binary) + || (fnl && *zCuvar_echonl != '\0')) + { + long iend; + + iend = ixsysdep_time ((long *) NULL) + (long) cCuvar_timeout; + for (zget = zsendbuf; zget < zput; zget++) + { + int bread; + int bwant; + + if (fCuvar_binary ? *zget == '\n' : *zget == '\r') + { + bwant = *zCuvar_echonl; + if (bwant == '\0') + continue; + } + else + { + if (! fCuvar_echocheck || ! isprint (*zget)) + continue; + bwant = *zget; + } + + do + { + if (FGOT_SIGNAL ()) + { + /* Make sure the signal is logged. */ + ubuffree (zsendbuf); + ulog (LOG_ERROR, (const char *) NULL); + ucuputs ("[file send aborted]"); + /* Reset the SIGINT flag so that it does not + confuse us in the future. */ + afSignal[INDEXSIG_SIGINT] = FALSE; + return FALSE; + } + + bread = breceive_char (qconn, + iend - ixsysdep_time ((long *) NULL), + TRUE); + if (bread < 0) + { + if (bread == -2) + ucuabort (); + + /* If we timed out, and we're not in binary + mode, we kill the line and try sending it + again from the beginning. */ + if (! fCuvar_binary && *zCuvar_kill != '\0') + { + ++ctries; + if (ctries < cCuvar_resend) + { + if (fCuvar_verbose) + { + printf ("R "); + (void) fflush (stdout); + } + if (! fsend_data (qconn, zCuvar_kill, 1, + TRUE)) + ucuabort (); + zbuf = zbufarg; + cbuf = cbufarg; + break; + } + } + ubuffree (zsendbuf); + ucuputs ("[timed out looking for echo]"); + return FALSE; + } + } + while (bread != *zget); + + if (bread < 0) + break; + } + } + } + + ubuffree (zsendbuf); + + return TRUE; +} |