1/* cu.c
2   Call up a remote system.
3
4   Copyright (C) 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#if USE_RCS_ID
28const char cu_rcsid[] = "$Id: cu.c,v 1.47 2002/03/05 19:10:41 ian Rel $";
29#endif
30
31#include "cu.h"
32#include "uudefs.h"
33#include "uuconf.h"
34#include "conn.h"
35#include "prot.h"
36#include "system.h"
37#include "sysdep.h"
38#include "getopt.h"
39
40#include <stdio.h>
41#include <ctype.h>
42#include <errno.h>
43
44/* Here are the user settable variables.  The user is permitted to
45   change these while running the program, using ~s.  */
46
47/* The escape character used to introduce a special command.  The
48   escape character is the first character of this string.  */
49const char *zCuvar_escape = "~";
50
51/* Whether to delay for a second before printing the host name after
52   seeing an escape character.  */
53boolean fCuvar_delay = TRUE;
54
55/* The input characters which finish a line.  The escape character is
56   only recognized following one of these characters.  The default is
57   carriage return, ^U, ^C, ^O, ^D, ^S, ^Q, ^R, which I got from the
58   Ultrix /etc/remote file.  */
59const char *zCuvar_eol = "\r\025\003\017\004\023\021\022";
60
61/* Whether to transfer binary data (nonprintable characters other than
62   newline and tab) when sending a file.  If this is FALSE, then
63   newline is changed to carriage return.  */
64boolean fCuvar_binary = FALSE;
65
66/* A prefix string to use before sending a binary character from a
67   file; this is only used if fCuvar_binary is TRUE.  The default is
68   ^V. */
69const char *zCuvar_binary_prefix = "\026";
70
71/* Whether to check for echoes of characters sent when sending a file.
72   This is ignored if fCuvar_binary is TRUE.  */
73boolean fCuvar_echocheck = FALSE;
74
75/* A character to look for after each newline is sent when sending a
76   file.  The character is the first character in this string, except
77   that a '\0' means that no echo check is done.  */
78const char *zCuvar_echonl = "\r";
79
80/* The timeout to use when looking for an character.  */
81int cCuvar_timeout = 30;
82
83/* The character to use to kill a line if an echo check fails.  The
84   first character in this string is sent.  The default is ^U.  */
85const char *zCuvar_kill = "\025";
86
87/* The number of times to try resending a line if the echo check keeps
88   failing.  */
89int cCuvar_resend = 10;
90
91/* The string to send at the end of a file sent with ~>.  The default
92   is ^D.  */
93const char *zCuvar_eofwrite = "\004";
94
95/* The string to look for to finish a file received with ~<.  For tip
96   this is a collection of single characters, but I don't want to do
97   that because it means that there are characters which cannot be
98   received.  The default is a guess at a typical shell prompt.  */
99const char *zCuvar_eofread = "$";
100
101/* Whether to provide verbose information when sending or receiving a
102   file.  */
103boolean fCuvar_verbose = TRUE;
104
105/* The table used to give a value to a variable, and to print all the
106   variable values.  */
107
108static const struct uuconf_cmdtab asCuvars[] =
109{
110  { "escape", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_escape, NULL },
111  { "delay", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_delay, NULL },
112  { "eol", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eol, NULL },
113  { "binary", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_binary, NULL },
114  { "binary-prefix", UUCONF_CMDTABTYPE_STRING,
115      (pointer) &zCuvar_binary_prefix, NULL },
116  { "echocheck", UUCONF_CMDTABTYPE_BOOLEAN,
117      (pointer) &fCuvar_echocheck, NULL },
118  { "echonl", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_echonl, NULL },
119  { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_timeout, NULL },
120  { "kill", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_kill, NULL },
121  { "resend", UUCONF_CMDTABTYPE_INT, (pointer) &cCuvar_resend, NULL },
122  { "eofwrite", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofwrite, NULL },
123  { "eofread", UUCONF_CMDTABTYPE_STRING, (pointer) &zCuvar_eofread, NULL },
124  { "verbose", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fCuvar_verbose, NULL },
125  { NULL, 0, NULL, NULL}
126};
127
128/* The string printed at the initial connect.  */
129#if ANSI_C
130#define ZCONNMSG "\aConnected."
131#else
132#define ZCONNMSG "Connected."
133#endif
134
135/* The string printed when disconnecting.  */
136#if ANSI_C
137#define ZDISMSG "\aDisconnected."
138#else
139#define ZDISMSG "Disconnected."
140#endif
141
142/* Local variables.  */
143
144/* The string we print when the user is once again connected to the
145   port after transferring a file or taking some other action.  */
146static const char abCuconnected[]
147#if ANSI_C
148  = "\a[connected]";
149#else
150  = "[connected]";
151#endif
152
153/* Global uuconf pointer.  */
154static pointer pCuuuconf;
155
156/* Connection.  */
157static struct sconnection *qCuconn;
158
159/* Whether to close the connection.  */
160static boolean fCuclose_conn;
161
162/* Dialer used to dial out.  */
163static struct uuconf_dialer *qCudialer;
164
165/* Whether we need to restore the terminal.  */
166static boolean fCurestore_terminal;
167
168/* Whether we are doing local echoing.  */
169static boolean fCulocalecho;
170
171/* Whether we need to call fsysdep_cu_finish.  */
172static boolean fCustarted;
173
174/* Whether ZCONNMSG has been printed yet.  */
175static boolean fCuconnprinted = FALSE;
176
177/* A structure used to pass information to icuport_lock.  */
178struct sconninfo
179{
180  boolean fmatched;
181  boolean flocked;
182  boolean fdirect;
183  struct sconnection *qconn;
184  const char *zline;
185};
186
187/* Local functions.  */
188
189static void ucuusage P((void));
190static void ucuhelp P((void));
191static void ucuabort P((void));
192static void uculog_start P((void));
193static void uculog_end P((void));
194static int icuport_lock P((struct uuconf_port *qport, pointer pinfo));
195static boolean fcudo_cmd P((pointer puuconf, struct sconnection *qconn,
196			    int bcmd));
197static boolean fcuset_var P((pointer puuconf, char *zline));
198static int icuunrecogvar P((pointer puuconf, int argc, char **argv,
199			    pointer pvar, pointer pinfo));
200static int icuunrecogfn P((pointer puuconf, int argc, char **argv,
201			   pointer pvar, pointer pinfo));
202static void uculist_vars P((void));
203static void uculist_fns P((const char *zescape));
204static boolean fcudo_subcmd P((pointer puuconf, struct sconnection *qconn,
205			       char *zline));
206static boolean fcusend_buf P((struct sconnection *qconn, const char *zbuf,
207			      size_t cbuf));
208
209#define ucuputs(zline) \
210       do { if (! fsysdep_terminal_puts (zline)) ucuabort (); } while (0)
211
212/* Long getopt options.  */
213static const struct option asCulongopts[] =
214{
215  { "phone", required_argument, NULL, 'c' },
216  { "escape", required_argument, NULL, 'E' },
217  { "parity", required_argument, NULL, 2 },
218  { "halfduplex", no_argument, NULL, 'h' },
219  { "prompt", no_argument, NULL, 'n' },
220  { "line", required_argument, NULL, 'l' },
221  { "port", required_argument, NULL, 'p' },
222  { "speed", required_argument, NULL, 's' },
223  { "baud", required_argument, NULL, 's' },
224  { "mapcr", no_argument, NULL, 't' },
225  { "nostop", no_argument, NULL, 3 },
226  { "system", required_argument, NULL, 'z' },
227  { "config", required_argument, NULL, 'I' },
228  { "debug", required_argument, NULL, 'x' },
229  { "version", no_argument, NULL, 'v' },
230  { "help", no_argument, NULL, 1 },
231  { NULL, 0, NULL, 0 }
232};
233
234int
235main (argc, argv)
236     int argc;
237     char **argv;
238{
239  /* -c: phone number.  */
240  char *zphone = NULL;
241  /* -e: even parity.  */
242  boolean feven = FALSE;
243  /* -l: line.  */
244  char *zline = NULL;
245  /* -n: prompt for phone number.  */
246  boolean fprompt = FALSE;
247  /* -o: odd parity.  */
248  boolean fodd = FALSE;
249  /* -p: port name.  */
250  const char *zport = NULL;
251  /* -s: speed.  */
252  long ibaud = 0L;
253  /* -t: map cr to crlf.  */
254  boolean fmapcr = FALSE;
255  /* -z: system.  */
256  const char *zsystem = NULL;
257  /* --nostop: turn off XON/XOFF.  */
258  enum txonxoffsetting txonxoff = XONXOFF_ON;
259  /* -I: configuration file name.  */
260  const char *zconfig = NULL;
261  int iopt;
262  pointer puuconf;
263  int iuuconf;
264  const char *zlocalname;
265  int i;
266  struct uuconf_system ssys;
267  const struct uuconf_system *qsys = NULL;
268  boolean flooped;
269  struct uuconf_port sport;
270  struct sconnection sconn;
271  struct sconninfo sinfo;
272  long ihighbaud;
273  struct uuconf_dialer sdialer;
274  struct uuconf_dialer *qdialer;
275  char bcmd;
276
277  if (argc < 1)
278  {
279      zProgram = "cu";
280      ucuusage ();
281  }
282
283  zProgram = argv[0];
284
285  /* We want to accept -# as a speed.  It's easiest to look through
286     the arguments, replace -# with -s#, and let getopt handle it.  */
287  for (i = 1; i < argc; i++)
288    {
289      if (argv[i][0] == '-'
290	  && isdigit (BUCHAR (argv[i][1])))
291	{
292	  size_t clen;
293	  char *z;
294
295	  clen = strlen (argv[i]);
296	  z = zbufalc (clen + 2);
297	  z[0] = '-';
298	  z[1] = 's';
299	  memcpy (z + 2, argv[i] + 1, clen);
300 	  argv[i] = z;
301	}
302    }
303
304  while ((iopt = getopt_long (argc, argv, "a:c:deE:hnI:l:op:s:tvx:z:",
305			      asCulongopts, (int *) NULL)) != EOF)
306    {
307      switch (iopt)
308	{
309	case 'c':
310	  /* Phone number.  */
311	  zphone = optarg;
312	  break;
313
314	case 'd':
315	  /* Set debugging level to maximum.  */
316#if DEBUG > 1
317	  iDebug = DEBUG_MAX;
318#endif
319	  break;
320
321	case 'e':
322	  /* Even parity.  */
323	  feven = TRUE;
324	  break;
325
326	case 'E':
327	  /* Escape character.  */
328	  zCuvar_escape = optarg;
329	  break;
330
331	case 'h':
332	  /* Local echo.  */
333	  fCulocalecho = TRUE;
334	  break;
335
336	case 'n':
337	  /* Prompt for phone number.  */
338	  fprompt = TRUE;
339	  break;
340
341	case 'l':
342	  /* Line name.  */
343	  zline = optarg;
344	  break;
345
346	case 'o':
347	  /* Odd parity.  */
348	  fodd = TRUE;
349	  break;
350
351	case 'p':
352	case 'a':
353	  /* Port name (-a is for compatibility).  */
354	  zport = optarg;
355	  break;
356
357	case 's':
358	  /* Speed.  */
359	  ibaud = strtol (optarg, (char **) NULL, 10);
360	  break;
361
362	case 't':
363	  /* Map cr to crlf.  */
364	  fmapcr = TRUE;
365	  break;
366
367	case 'z':
368	  /* System name.  */
369	  zsystem = optarg;
370	  break;
371
372	case 'I':
373	  /* Configuration file name.  */
374	  if (fsysdep_other_config (optarg))
375	    zconfig = optarg;
376	  break;
377
378	case 'x':
379#if DEBUG > 1
380	  /* Set debugging level.  */
381	  iDebug |= idebug_parse (optarg);
382#endif
383	  break;
384
385	case 'v':
386	  /* Print version and exit.  */
387	  printf ("cu (Taylor UUCP) %s\n", VERSION);
388	  printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n");
389	  printf ("This program is free software; you may redistribute it under the terms of\n");
390	  printf ("the GNU General Public LIcense.  This program has ABSOLUTELY NO WARRANTY.\n");
391	  exit (EXIT_SUCCESS);
392	  /*NOTREACHED*/
393
394	case 2:
395	  /* --parity.  */
396	  if (strncmp (optarg, "even", strlen (optarg)) == 0)
397	    feven = TRUE;
398	  else if (strncmp (optarg, "odd", strlen (optarg)) == 0)
399	    fodd = TRUE;
400	  else if (strncmp (optarg, "none", strlen (optarg)) == 0)
401	    {
402	      feven = TRUE;
403	      fodd = TRUE;
404	    }
405	  else
406	    {
407	      fprintf (stderr, "%s: --parity requires even, odd or none\n",
408		       zProgram);
409	      ucuusage ();
410	    }
411	  break;
412
413	case 3:
414	  /* --nostop.  */
415	  txonxoff = XONXOFF_OFF;
416	  break;
417
418	case 1:
419	  /* --help.  */
420	  ucuhelp ();
421	  exit (EXIT_SUCCESS);
422	  /*NOTREACHED*/
423
424	case 0:
425	  /* Long option found and flag set.  */
426	  break;
427
428	default:
429	  ucuusage ();
430	  /*NOTREACHED*/
431	}
432    }
433
434  /* There can be one more argument, which is either a system name, a
435     phone number, or "dir".  We decide which it is based on the first
436     character.  To call a UUCP system whose name begins with a digit,
437     or one which is named "dir", you must use -z.  */
438  if (optind != argc)
439    {
440      if (optind != argc - 1
441	  || zsystem != NULL
442	  || zphone != NULL)
443	{
444	  fprintf (stderr, "%s: too many arguments\n", zProgram);
445	  ucuusage ();
446	}
447      if (strcmp (argv[optind], "dir") != 0)
448	{
449	  if (isdigit (BUCHAR (argv[optind][0])))
450	    zphone = argv[optind];
451	  else
452	    zsystem = argv[optind];
453	}
454    }
455
456  /* If the user doesn't give a system, port, line or speed, then
457     there's no basis on which to select a port.  */
458  if (zsystem == NULL
459      && zport == NULL
460      && zline == NULL
461      && ibaud == 0L)
462    {
463      fprintf (stderr, "%s: must specify system, line, port or speed\n",
464	       zProgram);
465      ucuusage ();
466    }
467
468  if (fprompt)
469    {
470      size_t cphone;
471
472      printf ("Phone number: ");
473      (void) fflush (stdout);
474      zphone = NULL;
475      cphone = 0;
476      if (getline (&zphone, &cphone, stdin) <= 0
477	  || *zphone == '\0')
478	{
479	  fprintf (stderr, "%s: no phone number entered\n", zProgram);
480	  exit (EXIT_FAILURE);
481	}
482    }
483
484  iuuconf = uuconf_init (&puuconf, "cu", zconfig);
485  if (iuuconf != UUCONF_SUCCESS)
486    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
487  pCuuuconf = puuconf;
488
489#if DEBUG > 1
490  {
491    const char *zdebug;
492
493    iuuconf = uuconf_debuglevel (puuconf, &zdebug);
494    if (iuuconf != UUCONF_SUCCESS)
495      ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
496    if (zdebug != NULL)
497      iDebug |= idebug_parse (zdebug);
498  }
499#endif
500
501  usysdep_initialize (puuconf, INIT_NOCHDIR | INIT_SUID);
502
503  iuuconf = uuconf_localname (puuconf, &zlocalname);
504  if (iuuconf == UUCONF_NOT_FOUND)
505    {
506      zlocalname = zsysdep_localname ();
507      if (zlocalname == NULL)
508	exit (EXIT_FAILURE);
509    }
510  else if (iuuconf != UUCONF_SUCCESS)
511    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
512
513  ulog_fatal_fn (ucuabort);
514  pfLstart = uculog_start;
515  pfLend = uculog_end;
516
517#ifdef SIGINT
518  usysdep_signal (SIGINT);
519#endif
520#ifdef SIGHUP
521  usysdep_signal (SIGHUP);
522#endif
523#ifdef SIGQUIT
524  usysdep_signal (SIGQUIT);
525#endif
526#ifdef SIGTERM
527  usysdep_signal (SIGTERM);
528#endif
529#ifdef SIGPIPE
530  usysdep_signal (SIGPIPE);
531#endif
532
533  if (zsystem != NULL)
534    {
535      iuuconf = uuconf_system_info (puuconf, zsystem, &ssys);
536      if (iuuconf != UUCONF_SUCCESS)
537	{
538	  if (iuuconf != UUCONF_NOT_FOUND)
539	    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
540	  ulog (LOG_FATAL, "%s: System not found", zsystem);
541	}
542      qsys = &ssys;
543    }
544
545  /* This loop is used if a system is specified.  It loops over the
546     various alternates until it finds one for which the dial
547     succeeds.  This is an ugly spaghetti construction, and it should
548     be broken up into different functions someday.  */
549  flooped = FALSE;
550  while (TRUE)
551    {
552      enum tparitysetting tparity;
553      enum tstripsetting tstrip;
554      long iusebaud;
555
556      /* The uuconf_find_port function only selects directly on a port
557	 name and a speed.  To select based on the line name, we use a
558	 function.  If we can't find any defined port, and the user
559	 specified a line name but did not specify a port name or a
560	 system or a phone number, then we fake a direct port with
561	 that line name (we don't fake a port if a system or phone
562	 number were given because if we fake a port we have no way to
563	 place a call; perhaps we should automatically look up a
564	 particular dialer).  This permits users to say cu -lttyd0
565	 without having to put ttyd0 in the ports file, provided they
566	 have read and write access to the port.  */
567      sinfo.fmatched = FALSE;
568      sinfo.flocked = FALSE;
569      sinfo.fdirect = qsys == NULL && zphone == NULL;
570      sinfo.qconn = &sconn;
571      sinfo.zline = zline;
572      if (zport != NULL || zline != NULL || ibaud != 0L)
573	{
574	  iuuconf = uuconf_find_port (puuconf, zport, ibaud, 0L,
575				      icuport_lock, (pointer) &sinfo,
576				      &sport);
577	  if (iuuconf != UUCONF_SUCCESS)
578	    {
579	      if (iuuconf != UUCONF_NOT_FOUND)
580		{
581		  if (sinfo.flocked)
582		    {
583		      (void) fconn_unlock (&sconn);
584		      uconn_free (&sconn);
585		    }
586		  ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
587		}
588	      if (zline == NULL
589		  || zport != NULL
590		  || zphone != NULL
591		  || qsys != NULL)
592		{
593		  if (sinfo.fmatched)
594		    ulog (LOG_FATAL, "All matching ports in use");
595		  else
596		    ulog (LOG_FATAL, "No matching ports");
597		}
598
599	      sport.uuconf_zname = zline;
600	      sport.uuconf_ttype = UUCONF_PORTTYPE_DIRECT;
601	      sport.uuconf_zprotocols = NULL;
602	      sport.uuconf_qproto_params = NULL;
603	      sport.uuconf_ireliable = 0;
604	      sport.uuconf_zlockname = NULL;
605	      sport.uuconf_palloc = NULL;
606	      sport.uuconf_u.uuconf_sdirect.uuconf_zdevice = NULL;
607	      sport.uuconf_u.uuconf_sdirect.uuconf_ibaud = ibaud;
608
609	      if (! fconn_init (&sport, &sconn, UUCONF_PORTTYPE_UNKNOWN))
610		ucuabort ();
611
612	      if (! fconn_lock (&sconn, FALSE, TRUE))
613		ulog (LOG_FATAL, "%s: Line in use", zline);
614
615	      qCuconn = &sconn;
616
617	      /* Check user access after locking the port, because on
618		 some systems shared lines affect the ownership and
619		 permissions.  In such a case ``Line in use'' is more
620		 clear than ``Permission denied.''  */
621	      if (! fsysdep_port_access (&sport))
622		ulog (LOG_FATAL, "%s: Permission denied", zline);
623	    }
624	  iusebaud = ibaud;
625	  ihighbaud = 0L;
626	}
627      else
628	{
629	  for (; qsys != NULL; qsys = qsys->uuconf_qalternate)
630	    {
631	      if (! qsys->uuconf_fcall)
632		continue;
633	      if (qsys->uuconf_qport != NULL)
634		{
635		  if (fconn_init (qsys->uuconf_qport, &sconn,
636				  UUCONF_PORTTYPE_UNKNOWN))
637		    {
638		      if (fconn_lock (&sconn, FALSE, FALSE))
639			{
640			  qCuconn = &sconn;
641			  break;
642			}
643		      uconn_free (&sconn);
644		    }
645		}
646	      else
647		{
648		  sinfo.fmatched = FALSE;
649		  sinfo.flocked = FALSE;
650		  sinfo.qconn = &sconn;
651		  iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport,
652					      qsys->uuconf_ibaud,
653					      qsys->uuconf_ihighbaud,
654					      icuport_lock,
655					      (pointer) &sinfo,
656					      &sport);
657		  if (iuuconf == UUCONF_SUCCESS)
658		    break;
659		  if (iuuconf != UUCONF_NOT_FOUND)
660		    {
661		      if (sinfo.flocked)
662			{
663			  (void) fconn_unlock (&sconn);
664			  uconn_free (&sconn);
665			}
666		      ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
667		    }
668		}
669	    }
670
671	  if (qsys == NULL)
672	    {
673	      const char *zrem;
674
675	      if (flooped)
676		zrem = "remaining ";
677	      else
678		zrem = "";
679	      if (sinfo.fmatched)
680		ulog (LOG_FATAL, "%s: All %smatching ports in use",
681		      zsystem, zrem);
682	      else
683		ulog (LOG_FATAL, "%s: No %smatching ports", zsystem, zrem);
684	    }
685
686	  iusebaud = qsys->uuconf_ibaud;
687	  ihighbaud = qsys->uuconf_ihighbaud;
688	}
689
690      /* Here we have locked a connection to use.  */
691      if (! fconn_open (&sconn, iusebaud, ihighbaud, FALSE, sinfo.fdirect))
692	ucuabort ();
693
694      fCuclose_conn = TRUE;
695
696      if (FGOT_SIGNAL ())
697	ucuabort ();
698
699      /* Set up the connection.  */
700      if (fodd && feven)
701	{
702	  tparity = PARITYSETTING_NONE;
703	  tstrip = STRIPSETTING_SEVENBITS;
704	}
705      else if (fodd)
706	{
707	  tparity = PARITYSETTING_ODD;
708	  tstrip = STRIPSETTING_SEVENBITS;
709	}
710      else if (feven)
711	{
712	  tparity = PARITYSETTING_EVEN;
713	  tstrip = STRIPSETTING_SEVENBITS;
714	}
715      else
716	{
717	  tparity = PARITYSETTING_DEFAULT;
718	  tstrip = STRIPSETTING_DEFAULT;
719	}
720
721      if (! fconn_set (&sconn, tparity, tstrip, txonxoff))
722	ucuabort ();
723
724      if (qsys != NULL)
725	zphone = qsys->uuconf_zphone;
726
727      if (qsys != NULL || zphone != NULL)
728	{
729	  enum tdialerfound tdialer;
730
731	  if (! fconn_dial (&sconn, puuconf, qsys, zphone, &sdialer,
732			    &tdialer))
733	    {
734	      if (zport != NULL
735		  || zline != NULL
736		  || ibaud != 0L
737		  || qsys == NULL)
738		ucuabort ();
739
740	      qsys = qsys->uuconf_qalternate;
741	      if (qsys == NULL)
742		ulog (LOG_FATAL, "%s: No remaining alternates", zsystem);
743
744	      fCuclose_conn = FALSE;
745	      (void) fconn_close (&sconn, pCuuuconf, qCudialer, FALSE);
746	      qCuconn = NULL;
747	      (void) fconn_unlock (&sconn);
748	      uconn_free (&sconn);
749
750	      /* Loop around and try another alternate.  */
751	      flooped = TRUE;
752	      continue;
753	    }
754	  if (tdialer == DIALERFOUND_FALSE)
755	    qdialer = NULL;
756	  else
757	    qdialer = &sdialer;
758	}
759      else
760	{
761	  /* If no system or phone number was specified, we connect
762	     directly to the modem.  We only permit this if the user
763	     has access to the port, since it permits various
764	     shenanigans such as reprogramming the automatic
765	     callbacks.  */
766	  if (! fsysdep_port_access (sconn.qport))
767	    ulog (LOG_FATAL, "Access to port denied");
768	  qdialer = NULL;
769	  if (! fconn_carrier (&sconn, FALSE))
770	    ulog (LOG_FATAL, "Can't turn off carrier");
771	}
772
773      break;
774    }
775
776  qCudialer = qdialer;
777
778  if (FGOT_SIGNAL ())
779    ucuabort ();
780
781  /* Here we have connected, and can start the main cu protocol.  The
782     program spends most of its time in system dependent code, and
783     only comes out when a special command is received from the
784     terminal.  */
785  printf ("%s\n", ZCONNMSG);
786  fCuconnprinted = TRUE;
787
788  if (! fsysdep_terminal_raw (fCulocalecho))
789    ucuabort ();
790
791  fCurestore_terminal = TRUE;
792
793  if (! fsysdep_cu_init (&sconn))
794    ucuabort ();
795
796  fCustarted = TRUE;
797
798  while (fsysdep_cu (&sconn, &bcmd, zlocalname))
799    if (! fcudo_cmd (puuconf, &sconn, bcmd))
800      break;
801
802  fCustarted = FALSE;
803  if (! fsysdep_cu_finish ())
804    ucuabort ();
805
806  fCurestore_terminal = FALSE;
807  (void) fsysdep_terminal_restore ();
808
809  (void) fconn_close (&sconn, puuconf, qdialer, TRUE);
810  (void) fconn_unlock (&sconn);
811  uconn_free (&sconn);
812
813  if (fCuconnprinted)
814    printf ("\n%s\n", ZDISMSG);
815
816  ulog_close ();
817
818  usysdep_exit (TRUE);
819
820  /* Avoid errors about not returning a value.  */
821  return 0;
822}
823
824/* Print a usage message and die.  */
825
826static void
827ucuusage ()
828{
829  fprintf (stderr, "Usage: %s [options] [system or phone-number]\n",
830	   zProgram);
831  fprintf (stderr, "Use %s --help for help\n", zProgram);
832  exit (EXIT_FAILURE);
833}
834
835/* Print a help message.  */
836
837static void
838ucuhelp ()
839{
840  printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n",
841	  VERSION);
842  printf ("Usage: %s [options] [system or phone-number]\n", zProgram);
843  printf (" -a,-p,--port port: Use named port\n");
844  printf (" -l,--line line: Use named device (e.g. tty0)\n");
845  printf (" -s,--speed,--baud speed, -#: Use given speed\n");
846  printf (" -c,--phone phone: Phone number to call\n");
847  printf (" -z,--system system: System to call\n");
848  printf (" -e: Set even parity\n");
849  printf (" -o: Set odd parity\n");
850  printf (" --parity={odd,even}: Set parity\n");
851  printf (" -E,--escape char: Set escape character\n");
852  printf (" -h,--halfduplex: Echo locally\n");
853  printf (" --nostop: Turn off XON/XOFF handling\n");
854  printf (" -t,--mapcr: Map carriage return to carriage return/linefeed\n");
855  printf (" -n,--prompt: Prompt for phone number\n");
856  printf (" -d: Set maximum debugging level\n");
857  printf (" -x,--debug debug: Set debugging type\n");
858#if HAVE_TAYLOR_CONFIG
859  printf (" -I,--config file: Set configuration file to use\n");
860#endif /* HAVE_TAYLOR_CONFIG */
861  printf (" -v,--version: Print version and exit\n");
862  printf (" --help: Print help and exit\n");
863  printf ("Report bugs to taylor-uucp@gnu.org\n");
864}
865
866/* This function is called when a fatal error occurs.  */
867
868static void
869ucuabort ()
870{
871  if (fCustarted)
872    {
873      fCustarted = FALSE;
874      (void) fsysdep_cu_finish ();
875    }
876
877  if (fCurestore_terminal)
878    {
879      fCurestore_terminal = FALSE;
880      (void) fsysdep_terminal_restore ();
881    }
882
883  if (qCuconn != NULL)
884    {
885      struct sconnection *qconn;
886
887      if (fCuclose_conn)
888	{
889	  fCuclose_conn = FALSE;
890	  (void) fconn_close (qCuconn, pCuuuconf, qCudialer, FALSE);
891	}
892      qconn = qCuconn;
893      qCuconn = NULL;
894      (void) fconn_unlock (qconn);
895      uconn_free (qconn);
896    }
897
898  ulog_close ();
899
900  if (fCuconnprinted)
901    printf ("\n%s\n", ZDISMSG);
902
903  usysdep_exit (FALSE);
904}
905
906/* This variable is just used to communicate between uculog_start and
907   uculog_end.  */
908static boolean fCulog_restore;
909
910/* This function is called by ulog before it output anything.  We use
911   it to restore the terminal, if necessary.  ulog is only called for
912   errors or debugging in cu, so it's not too costly to do this.  If
913   we didn't do it, then at least on Unix each line would leave the
914   cursor in the same column rather than wrapping back to the start,
915   since CRMOD will not be on.  */
916
917static void
918uculog_start ()
919{
920  if (! fCurestore_terminal)
921    fCulog_restore = FALSE;
922  else
923    {
924      fCulog_restore = TRUE;
925      fCurestore_terminal = FALSE;
926      if (! fsysdep_terminal_restore ())
927	ucuabort ();
928    }
929}
930
931/* This function is called by ulog after everything is output.  It
932   sets the terminal back, if necessary.  */
933
934static void
935uculog_end ()
936{
937  if (fCulog_restore)
938    {
939      if (! fsysdep_terminal_raw (fCulocalecho))
940	ucuabort ();
941      fCurestore_terminal = TRUE;
942    }
943}
944
945/* Check to see if this port has the desired line, to handle the -l
946   option.  If it does, or if no line was specified, set up a
947   connection and lock it.  */
948
949static int
950icuport_lock (qport, pinfo)
951     struct uuconf_port *qport;
952     pointer pinfo;
953{
954  struct sconninfo *q = (struct sconninfo *) pinfo;
955
956  if (q->zline != NULL
957      && ! fsysdep_port_is_line (qport, q->zline))
958    return UUCONF_NOT_FOUND;
959
960  q->fmatched = TRUE;
961
962  if (! fconn_init (qport, q->qconn, UUCONF_PORTTYPE_UNKNOWN))
963    return UUCONF_NOT_FOUND;
964  else if (! fconn_lock (q->qconn, FALSE, q->fdirect))
965    {
966      uconn_free (q->qconn);
967      return UUCONF_NOT_FOUND;
968    }
969  else
970    {
971      qCuconn = q->qconn;
972      q->flocked = TRUE;
973      return UUCONF_SUCCESS;
974    }
975}
976
977/* Execute a cu escape command.  Return TRUE if the connection should
978   continue, or FALSE if the connection should be terminated.  */
979
980static boolean
981fcudo_cmd (puuconf, qconn, bcmd)
982     pointer puuconf;
983     struct sconnection *qconn;
984     int bcmd;
985{
986  char *zline;
987  char *z;
988  char abescape[5];
989  boolean fret;
990  size_t clen;
991  char abbuf[100];
992
993  /* Some commands take a string up to the next newline character.  */
994  switch (bcmd)
995    {
996    default:
997      zline = NULL;
998      break;
999    case '!':
1000    case '$':
1001    case '|':
1002    case '+':
1003    case '%':
1004    case 'c':
1005    case '>':
1006    case '<':
1007    case 'p':
1008    case 't':
1009    case 's':
1010      {
1011	zline = zsysdep_terminal_line ((const char *) NULL);
1012	if (zline == NULL)
1013	  ucuabort ();
1014	zline[strcspn (zline, "\n")] = '\0';
1015      }
1016      break;
1017    }
1018
1019  switch (bcmd)
1020    {
1021    default:
1022      if (! isprint (*zCuvar_escape))
1023	sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape));
1024      else
1025	{
1026	  abescape[0] = *zCuvar_escape;
1027	  abescape[1] = '\0';
1028	}
1029      sprintf (abbuf, "[Unrecognized.  Use %s%s to send %s]",
1030	       abescape, abescape, abescape);
1031      ucuputs (abbuf);
1032      return TRUE;
1033
1034    case '.':
1035      /* Hangup.  */
1036      return FALSE;
1037
1038    case '!':
1039    case '$':
1040    case '|':
1041    case '+':
1042      /* Shell out.  */
1043      if (! fsysdep_cu_copy (FALSE)
1044	  || ! fsysdep_terminal_restore ())
1045	ucuabort ();
1046      fCurestore_terminal = FALSE;
1047      {
1048	enum tshell_cmd t;
1049
1050	switch (bcmd)
1051	  {
1052	  default:
1053	  case '!': t = SHELL_NORMAL; break;
1054	  case '$': t = SHELL_STDOUT_TO_PORT; break;
1055	  case '|': t = SHELL_STDIN_FROM_PORT; break;
1056	  case '+': t = SHELL_STDIO_ON_PORT; break;
1057	  }
1058
1059	(void) fsysdep_shell (qconn, zline, t);
1060      }
1061      if (! fsysdep_cu_copy (TRUE)
1062	  || ! fsysdep_terminal_raw (fCulocalecho))
1063	ucuabort ();
1064      fCurestore_terminal = TRUE;
1065      ubuffree (zline);
1066      return TRUE;
1067
1068    case '%':
1069      fret = fcudo_subcmd (puuconf, qconn, zline);
1070      ubuffree (zline);
1071      return fret;
1072
1073    case '#':
1074      if (! fconn_break (qconn))
1075	ucuabort ();
1076      return TRUE;
1077
1078    case 'c':
1079      (void) fsysdep_chdir (zline);
1080      ubuffree (zline);
1081      return TRUE;
1082
1083    case '>':
1084    case '<':
1085    case 'p':
1086    case 't':
1087      clen = strlen (zline);
1088      z = zbufalc (clen + 3);
1089      z[0] = bcmd;
1090      z[1] = ' ';
1091      memcpy (z + 2, zline, clen + 1);
1092      ubuffree (zline);
1093      fret = fcudo_subcmd (puuconf, qconn, z);
1094      ubuffree (z);
1095      return fret;
1096
1097    case 'z':
1098      if (! fsysdep_cu_copy (FALSE)
1099	  || ! fsysdep_terminal_restore ())
1100	ucuabort ();
1101      fCurestore_terminal = FALSE;
1102      if (! fsysdep_suspend ())
1103	ucuabort ();
1104      if (! fsysdep_cu_copy (TRUE)
1105	  || ! fsysdep_terminal_raw (fCulocalecho))
1106	ucuabort ();
1107      fCurestore_terminal = TRUE;
1108      return TRUE;
1109
1110    case 's':
1111      fret = fcuset_var (puuconf, zline);
1112      ubuffree (zline);
1113      return fret;
1114
1115    case 'v':
1116      uculist_vars ();
1117      return TRUE;
1118
1119    case '?':
1120      if (! isprint (*zCuvar_escape))
1121	sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape));
1122      else
1123	{
1124	  abescape[0] = *zCuvar_escape;
1125	  abescape[1] = '\0';
1126	}
1127      ucuputs ("");
1128      ucuputs ("[Escape sequences]");
1129      sprintf (abbuf,
1130	       "[%s. hangup]                   [%s!CMD run shell]",
1131	       abescape, abescape);
1132      ucuputs (abbuf);
1133      sprintf (abbuf,
1134	       "[%s$CMD stdout to remote]      [%s|CMD stdin from remote]",
1135	       abescape, abescape);
1136      ucuputs (abbuf);
1137      sprintf (abbuf,
1138	       "[%s+CMD stdin and stdout to remote]",
1139	       abescape);
1140      ucuputs (abbuf);
1141      sprintf (abbuf,
1142	       "[%s# send break]               [%scDIR change directory]",
1143	       abescape, abescape);
1144      ucuputs (abbuf);
1145      sprintf (abbuf,
1146	       "[%s> send file]                [%s< receive file]",
1147	       abescape, abescape);
1148      ucuputs (abbuf);
1149      sprintf (abbuf,
1150	       "[%spFROM TO send to Unix]      [%stFROM TO receive from Unix]",
1151	       abescape, abescape);
1152      ucuputs (abbuf);
1153      sprintf (abbuf,
1154	       "[%ssVAR VAL set variable]      [%ssVAR set boolean]",
1155	       abescape, abescape);
1156      ucuputs (abbuf);
1157      sprintf (abbuf,
1158	       "[%ss!VAR unset boolean]        [%sv list variables]",
1159	       abescape, abescape);
1160      ucuputs (abbuf);
1161#ifdef SIGTSTP
1162      sprintf (abbuf,
1163	       "[%sz suspend]",
1164	       abescape);
1165      ucuputs (abbuf);
1166#endif
1167      uculist_fns (abescape);
1168      return TRUE;
1169    }
1170}
1171
1172/* List ~% functions.  */
1173
1174static void
1175uculist_fns (zescape)
1176     const char *zescape;
1177{
1178  char abbuf[100];
1179
1180  sprintf (abbuf,
1181	   "[%s%%break send break]         [%s%%cd DIR change directory]",
1182	   zescape, zescape);
1183  ucuputs (abbuf);
1184  sprintf (abbuf,
1185	   "[%s%%put FROM TO send file]    [%s%%take FROM TO receive file]",
1186	   zescape, zescape);
1187  ucuputs (abbuf);
1188  sprintf (abbuf,
1189	   "[%s%%nostop no XON/XOFF]       [%s%%stop use XON/XOFF]",
1190	   zescape, zescape);
1191  ucuputs (abbuf);
1192}
1193
1194/* Set a variable.  */
1195
1196static boolean
1197fcuset_var (puuconf, zline)
1198     pointer puuconf;
1199     char *zline;
1200{
1201  char *zvar, *zval;
1202  char *azargs[2];
1203  int iuuconf;
1204
1205  zvar = strtok (zline, "= \t");
1206  if (zvar == NULL)
1207    {
1208      ucuputs (abCuconnected);
1209      return TRUE;
1210    }
1211
1212  zval = strtok ((char *) NULL, " \t");
1213
1214  if (zval == NULL)
1215    {
1216      azargs[0] = zvar;
1217      if (azargs[0][0] != '!')
1218	azargs[1] = zbufcpy ("t");
1219      else
1220	{
1221	  ++azargs[0];
1222	  azargs[1] = zbufcpy ("f");
1223	}
1224    }
1225  else
1226    {
1227      azargs[0] = zvar;
1228      azargs[1] = zbufcpy (zval);
1229    }
1230
1231  iuuconf = uuconf_cmd_args (puuconf, 2, azargs, asCuvars,
1232			     (pointer) NULL, icuunrecogvar, 0,
1233			     (pointer) NULL);
1234
1235  if ((iuuconf & UUCONF_CMDTABRET_KEEP) == 0)
1236    ubuffree (azargs[1]);
1237
1238  if ((iuuconf &~ UUCONF_CMDTABRET_KEEP) != UUCONF_SUCCESS)
1239    ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1240
1241  return TRUE;
1242}
1243
1244/* Warn about an unknown variable.  */
1245
1246/*ARGSUSED*/
1247static int
1248icuunrecogvar (puuconf, argc, argv, pvar, pinfo)
1249     pointer puuconf ATTRIBUTE_UNUSED;
1250     int argc ATTRIBUTE_UNUSED;
1251     char **argv;
1252     pointer pvar ATTRIBUTE_UNUSED;
1253     pointer pinfo ATTRIBUTE_UNUSED;
1254{
1255  char abescape[5];
1256
1257  if (! isprint (*zCuvar_escape))
1258    sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape));
1259  else
1260    {
1261      abescape[0] = *zCuvar_escape;
1262      abescape[1] = '\0';
1263    }
1264  ulog (LOG_ERROR, "%s: unknown variable (%sv lists variables)",
1265	argv[0], abescape);
1266  return UUCONF_CMDTABRET_CONTINUE;
1267}
1268
1269/* List all the variables with their values.  */
1270
1271static void
1272uculist_vars ()
1273{
1274  const struct uuconf_cmdtab *q;
1275  char abbuf[100];
1276
1277  ucuputs ("");
1278  for (q = asCuvars; q->uuconf_zcmd != NULL; q++)
1279    {
1280      switch (UUCONF_TTYPE_CMDTABTYPE (q->uuconf_itype))
1281	{
1282	case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_BOOLEAN):
1283	  if (*(boolean *) q->uuconf_pvar)
1284	    sprintf (abbuf, "%s true", q->uuconf_zcmd);
1285	  else
1286	    sprintf (abbuf, "%s false", q->uuconf_zcmd);
1287	  break;
1288
1289	case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_INT):
1290	  sprintf (abbuf, "%s %d", q->uuconf_zcmd, *(int *) q->uuconf_pvar);
1291	  break;
1292
1293	case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_LONG):
1294	  sprintf (abbuf, "%s %ld", q->uuconf_zcmd,
1295		   *(long *) q->uuconf_pvar);
1296	  break;
1297
1298	case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_STRING):
1299	case UUCONF_TTYPE_CMDTABTYPE (UUCONF_CMDTABTYPE_FULLSTRING):
1300	  {
1301	    const char *z;
1302	    char abchar[5];
1303	    size_t clen;
1304
1305	    sprintf (abbuf, "%s ", q->uuconf_zcmd);
1306	    clen = strlen (abbuf);
1307	    for (z = *(const char **) q->uuconf_pvar; *z != '\0'; z++)
1308	      {
1309		int cchar;
1310
1311		if (! isprint (*z))
1312		  {
1313		    sprintf (abchar, "\\%03o", BUCHAR (*z));
1314		    cchar = 4;
1315		  }
1316		else
1317		  {
1318		    abchar[0] = *z;
1319		    abchar[1] = '\0';
1320		    cchar = 1;
1321		  }
1322		if (clen + cchar < sizeof (abbuf))
1323		  strcat (abbuf, abchar);
1324		clen += cchar;
1325	      }
1326	  }
1327	  break;
1328
1329	default:
1330	  sprintf (abbuf, "%s [unprintable type]", q->uuconf_zcmd);
1331	  break;
1332	}
1333
1334      ucuputs (abbuf);
1335    }
1336}
1337
1338/* Subcommands.  These are commands that begin with ~%.  */
1339
1340/* This variable is only used so that we can pass a non-NULL address
1341   in pvar.  It is never assigned to or examined.  */
1342
1343static char bCutype;
1344
1345/* The command table for the subcommands.  */
1346
1347static int icubreak P((pointer puuconf, int argc, char **argv, pointer pvar,
1348		       pointer pinfo));
1349static int icudebug P((pointer puuconf, int argc, char **argv, pointer pvar,
1350		       pointer pinfo));
1351static int icuchdir P((pointer puuconf, int argc, char **argv, pointer pvar,
1352		       pointer pinfo));
1353static int icuput P((pointer puuconf, int argc, char **argv, pointer pvar,
1354		     pointer pinfo));
1355static int icutake P((pointer puuconf, int argc, char **argv, pointer pvar,
1356		      pointer pinfo));
1357static int icunostop P((pointer puuconf, int argc, char **argv, pointer pvar,
1358			pointer pinfo));
1359
1360static const struct uuconf_cmdtab asCucmds[] =
1361{
1362  { "break", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak },
1363  { "b", UUCONF_CMDTABTYPE_FN | 1, NULL, icubreak },
1364  { "cd", UUCONF_CMDTABTYPE_FN | 0, NULL, icuchdir },
1365  { "d", UUCONF_CMDTABTYPE_FN | 1, NULL, icudebug },
1366  { "put", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput },
1367  { "take", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake },
1368  { "nostop", UUCONF_CMDTABTYPE_FN | 1, NULL, icunostop },
1369  { "stop", UUCONF_CMDTABTYPE_FN | 1, &bCutype, icunostop },
1370  { ">", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icuput },
1371  { "<", UUCONF_CMDTABTYPE_FN | 0, &bCutype, icutake },
1372  { "p", UUCONF_CMDTABTYPE_FN | 0, NULL, icuput },
1373  { "t", UUCONF_CMDTABTYPE_FN | 0, NULL, icutake },
1374  { NULL, 0, NULL, NULL }
1375};
1376
1377/* Do a subcommand.  This is called by commands beginning with ~%.  */
1378
1379static boolean
1380fcudo_subcmd (puuconf, qconn, zline)
1381     pointer puuconf;
1382     struct sconnection *qconn;
1383     char *zline;
1384{
1385  char *azargs[3];
1386  int iarg;
1387  int iuuconf;
1388
1389  for (iarg = 0; iarg < 3; iarg++)
1390    {
1391      azargs[iarg] = strtok (iarg == 0 ? zline : (char *) NULL, " \t\n");
1392      if (azargs[iarg] == NULL)
1393	break;
1394    }
1395
1396  if (iarg == 0)
1397    {
1398      ucuputs (abCuconnected);
1399      return TRUE;
1400    }
1401
1402  iuuconf = uuconf_cmd_args (puuconf, iarg, azargs, asCucmds,
1403			     (pointer) qconn, icuunrecogfn,
1404			     0, (pointer) NULL);
1405  if (iuuconf != UUCONF_SUCCESS)
1406    ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1407
1408  return TRUE;
1409}
1410
1411/* Warn about an unknown function.  */
1412
1413/*ARGSUSED*/
1414static int
1415icuunrecogfn (puuconf, argc, argv, pvar, pinfo)
1416     pointer puuconf ATTRIBUTE_UNUSED;
1417     int argc ATTRIBUTE_UNUSED;
1418     char **argv;
1419     pointer pvar ATTRIBUTE_UNUSED;
1420     pointer pinfo ATTRIBUTE_UNUSED;
1421{
1422  char abescape[5];
1423
1424  if (! isprint (*zCuvar_escape))
1425    sprintf (abescape, "\\%03o", BUCHAR (*zCuvar_escape));
1426  else
1427    {
1428      abescape[0] = *zCuvar_escape;
1429      abescape[1] = '\0';
1430    }
1431  if (argv[0][0] == '?')
1432    uculist_fns (abescape);
1433  else
1434    ulog (LOG_ERROR, "%s: unknown (%s%%? lists choices)",
1435	  argv[0], abescape);
1436  return UUCONF_CMDTABRET_CONTINUE;
1437}
1438
1439/* Send a break.  */
1440
1441/*ARGSUSED*/
1442static int
1443icubreak (puuconf, argc, argv, pvar, pinfo)
1444     pointer puuconf ATTRIBUTE_UNUSED;
1445     int argc ATTRIBUTE_UNUSED;
1446     char **argv ATTRIBUTE_UNUSED;
1447     pointer pvar ATTRIBUTE_UNUSED;
1448     pointer pinfo;
1449{
1450  struct sconnection *qconn = (struct sconnection *) pinfo;
1451
1452  if (! fconn_break (qconn))
1453    ucuabort ();
1454  return UUCONF_CMDTABRET_CONTINUE;
1455}
1456
1457/* Change directories.  */
1458
1459/*ARGSUSED*/
1460static int
1461icuchdir (puuconf, argc, argv, pvar, pinfo)
1462     pointer puuconf ATTRIBUTE_UNUSED;
1463     int argc;
1464     char **argv;
1465     pointer pvar ATTRIBUTE_UNUSED;
1466     pointer pinfo ATTRIBUTE_UNUSED;
1467{
1468  const char *zarg;
1469
1470  if (argc <= 1)
1471    zarg = NULL;
1472  else
1473    zarg = argv[1];
1474  (void) fsysdep_chdir (zarg);
1475  return UUCONF_CMDTABRET_CONTINUE;
1476}
1477
1478/* Toggle debugging.  */
1479
1480/*ARGSUSED*/
1481static int
1482icudebug (puuconf, argc, argv, pvar, pinfo)
1483     pointer puuconf ATTRIBUTE_UNUSED;
1484     int argc ATTRIBUTE_UNUSED;
1485     char **argv ATTRIBUTE_UNUSED;
1486     pointer pvar ATTRIBUTE_UNUSED;
1487     pointer pinfo ATTRIBUTE_UNUSED;
1488{
1489#if DEBUG > 1
1490  if (iDebug != 0)
1491    iDebug = 0;
1492  else
1493    iDebug = DEBUG_MAX;
1494#else
1495  ucuputs ("[compiled without debugging]");
1496#endif
1497  return UUCONF_CMDTABRET_CONTINUE;
1498}
1499
1500/* Control whether the port does xon/xoff handshaking.  If pvar is not
1501   NULL, this is "stop"; otherwise it is "nostop".  */
1502
1503/*ARGSUSED*/
1504static int
1505icunostop (puuconf, argc, argv, pvar, pinfo)
1506     pointer puuconf ATTRIBUTE_UNUSED;
1507     int argc ATTRIBUTE_UNUSED;
1508     char **argv ATTRIBUTE_UNUSED;
1509     pointer pvar;
1510     pointer pinfo;
1511{
1512  struct sconnection *qconn = (struct sconnection *) pinfo;
1513
1514  if (! fconn_set (qconn, PARITYSETTING_DEFAULT, STRIPSETTING_DEFAULT,
1515		   pvar == NULL ? XONXOFF_OFF : XONXOFF_ON))
1516    ucuabort ();
1517  return UUCONF_CMDTABRET_CONTINUE;
1518}
1519
1520/* Send a file to the remote system.  The first argument is the file
1521   to send.  If that argument is not present, it is prompted for.  The
1522   second argument is to file name to use on the remote system.  If
1523   that argument is not present, the basename of the local filename is
1524   used.  If pvar is not NULL, then this is ~>, which is used to send
1525   a command to a non-Unix system.  We treat is the same as ~%put,
1526   except that we assume the user has already entered the appropriate
1527   command (for ~%put, we force ``cat >to'' to the other side).  */
1528
1529/*ARGSUSED*/
1530static int
1531icuput (puuconf, argc, argv, pvar, pinfo)
1532     pointer puuconf ATTRIBUTE_UNUSED;
1533     int argc;
1534     char **argv;
1535     pointer pvar;
1536     pointer pinfo;
1537{
1538  struct sconnection *qconn = (struct sconnection *) pinfo;
1539  char *zfrom;
1540  char *zto = NULL;
1541  char *zalc;
1542  openfile_t e;
1543  int cline;
1544  char *zbuf;
1545  size_t cbuf;
1546
1547  if (argc > 1)
1548    zfrom = zbufcpy (argv[1]);
1549  else
1550    {
1551      zfrom = zsysdep_terminal_line ("File to send: ");
1552      if (zfrom == NULL)
1553	ucuabort ();
1554      zfrom[strcspn (zfrom, " \t\n")] = '\0';
1555
1556      if (*zfrom == '\0')
1557	{
1558	  ubuffree (zfrom);
1559	  ucuputs (abCuconnected);
1560	  return UUCONF_CMDTABRET_CONTINUE;
1561	}
1562    }
1563
1564  if (pvar == NULL)
1565    {
1566      if (argc > 2)
1567	zto = zbufcpy (argv[2]);
1568      else
1569	{
1570	  char *zbase;
1571	  char *zprompt;
1572
1573	  zbase = zsysdep_base_name (zfrom);
1574	  if (zbase == NULL)
1575	    ucuabort ();
1576
1577	  zprompt = zbufalc (sizeof "Remote file name []: " +
1578			     strlen (zbase));
1579	  sprintf (zprompt, "Remote file name [%s]: ", zbase);
1580	  zto = zsysdep_terminal_line (zprompt);
1581	  ubuffree (zprompt);
1582	  if (zto == NULL)
1583	    ucuabort ();
1584
1585	  zto[strcspn (zto, " \t\n")] = '\0';
1586	  if (*zto != '\0')
1587	    ubuffree (zbase);
1588	  else
1589	    {
1590	      ubuffree (zto);
1591	      zto = zbase;
1592	    }
1593	}
1594    }
1595
1596  e = esysdep_user_fopen (zfrom, TRUE, fCuvar_binary);
1597  if (! ffileisopen (e))
1598    {
1599      const char *zerrstr;
1600
1601      if (pvar == NULL)
1602	ubuffree (zto);
1603      zerrstr = strerror (errno);
1604      zalc = zbufalc (strlen (zfrom) + sizeof ": " + strlen (zerrstr));
1605      sprintf (zalc, "%s: %s", zfrom, zerrstr);
1606      ubuffree (zfrom);
1607      ucuputs (zalc);
1608      ubuffree (zalc);
1609      ucuputs (abCuconnected);
1610      return UUCONF_CMDTABRET_CONTINUE;
1611    }
1612
1613  ubuffree (zfrom);
1614
1615  /* Tell the system dependent layer to stop copying data from the
1616     port to the terminal.  We want to read the echoes ourself.  Also
1617     permit the local user to generate signals.  */
1618  if (! fsysdep_cu_copy (FALSE)
1619      || ! fsysdep_terminal_signals (TRUE))
1620    ucuabort ();
1621
1622  /* If pvar is NULL, then we are sending a file to a Unix system.  We
1623     send over the command "cat > TO" to prepare it to receive.  If
1624     pvar is not NULL, the user is assumed to have set up whatever
1625     action was needed to receive the file.  */
1626  if (pvar == NULL)
1627    {
1628      boolean fret;
1629
1630      zalc = zbufalc (sizeof "cat > \n" + strlen (zto));
1631      sprintf (zalc, "cat > %s\n", zto);
1632      ubuffree (zto);
1633      fret = fcusend_buf (qconn, zalc, strlen (zalc));
1634      ubuffree (zalc);
1635      if (! fret)
1636	{
1637	  (void) ffileclose (e);
1638	  if (! fsysdep_cu_copy (TRUE)
1639	      || ! fsysdep_terminal_signals (FALSE))
1640	    ucuabort ();
1641	  ucuputs (abCuconnected);
1642	  return UUCONF_CMDTABRET_CONTINUE;
1643	}
1644    }
1645
1646  cline = 0;
1647
1648  zbuf = NULL;
1649  cbuf = 0;
1650
1651  while (TRUE)
1652    {
1653      char abbuf[512];
1654      size_t c;
1655
1656#if USE_STDIO
1657      if (fCuvar_binary)
1658#endif
1659	{
1660	  if (ffileeof (e))
1661	    break;
1662	  c = cfileread (e, abbuf, sizeof abbuf);
1663	  if (ffileioerror (e, c))
1664	    {
1665	      ucuputs ("[file read error]");
1666	      break;
1667	    }
1668	  if (c == 0)
1669	    break;
1670	  zbuf = abbuf;
1671	}
1672#if USE_STDIO
1673      else
1674	{
1675	  if (getline (&zbuf, &cbuf, e) <= 0)
1676	    {
1677	      xfree ((pointer) zbuf);
1678	      break;
1679	    }
1680	  c = strlen (zbuf);
1681	}
1682#endif
1683
1684      if (fCuvar_verbose)
1685	{
1686	  ++cline;
1687	  printf ("%d ", cline);
1688	  (void) fflush (stdout);
1689	}
1690
1691      if (! fcusend_buf (qconn, zbuf, c))
1692	{
1693	  if (! fCuvar_binary)
1694	    xfree ((pointer) zbuf);
1695	  (void) fclose (e);
1696	  if (! fsysdep_cu_copy (TRUE)
1697	      || ! fsysdep_terminal_signals (FALSE))
1698	    ucuabort ();
1699	  ucuputs (abCuconnected);
1700	  return UUCONF_CMDTABRET_CONTINUE;
1701	}
1702    }
1703
1704  (void) ffileclose (e);
1705
1706  if (pvar == NULL)
1707    {
1708      char beof;
1709
1710      beof = '\004';
1711      if (! fconn_write (qconn, &beof, 1))
1712	ucuabort ();
1713    }
1714  else
1715    {
1716      if (*zCuvar_eofwrite != '\0')
1717	{
1718	  if (! fconn_write (qconn, zCuvar_eofwrite,
1719			     strlen (zCuvar_eofwrite)))
1720	    ucuabort ();
1721	}
1722    }
1723
1724  if (fCuvar_verbose)
1725    ucuputs ("");
1726
1727  ucuputs ("[file transfer complete]");
1728
1729  if (! fsysdep_cu_copy (TRUE)
1730      || ! fsysdep_terminal_signals (FALSE))
1731    ucuabort ();
1732
1733  ucuputs (abCuconnected);
1734  return UUCONF_CMDTABRET_CONTINUE;
1735}
1736
1737/* Get a file from the remote side.  This is ~%take, or ~t, or ~<.
1738   The first two are assumed to be taking the file from a Unix system,
1739   so we force the command "cat FROM; echo  */
1740
1741/*ARGSUSED*/
1742static int
1743icutake (puuconf, argc, argv, pvar, pinfo)
1744     pointer puuconf ATTRIBUTE_UNUSED;
1745     int argc;
1746     char **argv;
1747     pointer pvar;
1748     pointer pinfo;
1749{
1750  struct sconnection *qconn = (struct sconnection *) pinfo;
1751  const char *zeof;
1752  char *zfrom, *zto, *zcmd;
1753  char *zalc;
1754  openfile_t e;
1755  char bcr;
1756  size_t ceoflen;
1757  char *zlook = NULL;
1758  size_t ceofhave;
1759  boolean ferr;
1760
1761  if (argc > 1)
1762    zfrom = zbufcpy (argv[1]);
1763  else
1764    {
1765      zfrom = zsysdep_terminal_line ("Remote file to retreive: ");
1766      if (zfrom == NULL)
1767	ucuabort ();
1768      zfrom[strcspn (zfrom, " \t\n")] = '\0';
1769      if (*zfrom == '\0')
1770	{
1771	  ubuffree (zfrom);
1772	  ucuputs (abCuconnected);
1773	  return UUCONF_CMDTABRET_CONTINUE;
1774	}
1775    }
1776
1777  if (argc > 2)
1778    zto = zbufcpy (argv[2]);
1779  else
1780    {
1781      char *zbase;
1782      char *zprompt;
1783
1784      zbase = zsysdep_base_name (zfrom);
1785      if (zbase == NULL)
1786	ucuabort ();
1787
1788      zprompt = zbufalc (sizeof "Local file name []: " + strlen (zbase));
1789      sprintf (zprompt, "Local file name [%s]: ", zbase);
1790      zto = zsysdep_terminal_line (zprompt);
1791      ubuffree (zprompt);
1792      if (zto == NULL)
1793	ucuabort ();
1794
1795      zto[strcspn (zto, " \t\n")] = '\0';
1796      if (*zto != '\0')
1797	ubuffree (zbase);
1798      else
1799	{
1800	  ubuffree (zto);
1801	  zto = zbase;
1802	}
1803    }
1804
1805  if (pvar != NULL)
1806    {
1807      zcmd = zsysdep_terminal_line ("Remote command to execute: ");
1808      if (zcmd == NULL)
1809	ucuabort ();
1810      zcmd[strcspn (zcmd, "\n")] = '\0';
1811      zeof = zCuvar_eofread;
1812    }
1813  else
1814    {
1815      zcmd = zbufalc (sizeof "cat ; echo; echo ////cuend////"
1816		      + strlen (zfrom));
1817      sprintf (zcmd, "cat %s; echo; echo ////cuend////", zfrom);
1818      zeof = "\n////cuend////\n";
1819    }
1820
1821  ubuffree (zfrom);
1822
1823  e = esysdep_user_fopen (zto, FALSE, fCuvar_binary);
1824  if (! ffileisopen (e))
1825    {
1826      const char *zerrstr;
1827
1828      ubuffree (zcmd);
1829      zerrstr = strerror (errno);
1830      zalc = zbufalc (strlen (zto) + sizeof ": " + strlen (zerrstr));
1831      sprintf (zalc, "%s: %s\n", zto, zerrstr);
1832      ucuputs (zalc);
1833      ubuffree (zalc);
1834      ucuputs (abCuconnected);
1835      ubuffree (zto);
1836      return UUCONF_CMDTABRET_CONTINUE;
1837    }
1838
1839  if (! fsysdep_cu_copy (FALSE)
1840      || ! fsysdep_terminal_signals (TRUE))
1841    ucuabort ();
1842
1843  if (! fconn_write (qconn, zcmd, strlen (zcmd)))
1844    ucuabort ();
1845  bcr = '\r';
1846  if (! fconn_write (qconn, &bcr, 1))
1847    ucuabort ();
1848
1849  ubuffree (zcmd);
1850
1851  /* Eliminated any previously echoed data to avoid confusion.  */
1852  iPrecstart = 0;
1853  iPrecend = 0;
1854
1855  /* If we're dealing with a Unix system, we can reliably discard the
1856     command.  Otherwise, the command will probably wind up in the
1857     file; too bad.  */
1858  if (pvar == NULL)
1859    {
1860      int b;
1861
1862      while ((b = breceive_char (qconn, cCuvar_timeout, TRUE)) != '\n')
1863	{
1864	  if (b == -2)
1865	    ucuabort ();
1866	  if (b < 0)
1867	    {
1868	      ucuputs ("[timed out waiting for newline]");
1869	      ucuputs (abCuconnected);
1870	      ubuffree (zto);
1871	      return UUCONF_CMDTABRET_CONTINUE;
1872	    }
1873	}
1874    }
1875
1876  ceoflen = strlen (zeof);
1877  zlook = zbufalc (ceoflen);
1878  ceofhave = 0;
1879  ferr = FALSE;
1880
1881  while (TRUE)
1882    {
1883      int b;
1884
1885      if (FGOT_SIGNAL ())
1886	{
1887	  /* Make sure the signal is logged.  */
1888	  ulog (LOG_ERROR, (const char *) NULL);
1889	  ucuputs ("[file receive aborted]");
1890	  /* Reset the SIGINT flag so that it does not confuse us in
1891	     the future.  */
1892	  afSignal[INDEXSIG_SIGINT] = FALSE;
1893	  break;
1894	}
1895
1896      b = breceive_char (qconn, cCuvar_timeout, TRUE);
1897      if (b == -2)
1898	ucuabort ();
1899      if (b < 0)
1900	{
1901	  if (ceofhave > 0)
1902	    (void) fwrite (zlook, sizeof (char), ceofhave, e);
1903	  ucuputs ("[timed out]");
1904	  break;
1905	}
1906
1907      if (b == '\r' && ! fCuvar_binary)
1908	continue;
1909
1910      if (ceoflen == 0)
1911	{
1912	  if (cfilewrite (e, &b, 1) != 1)
1913	    {
1914	      ferr = TRUE;
1915	      break;
1916	    }
1917	}
1918      else
1919	{
1920	  zlook[ceofhave] = b;
1921	  ++ceofhave;
1922	  if (ceofhave == ceoflen)
1923	    {
1924	      size_t cmove;
1925	      char *zmove;
1926
1927	      if (memcmp (zeof, zlook, ceoflen) == 0)
1928		{
1929		  ucuputs ("[file transfer complete]");
1930		  break;
1931		}
1932
1933	      if (cfilewrite (e, zlook, 1) != 1)
1934		{
1935		  ferr = TRUE;
1936		  break;
1937		}
1938
1939	      zmove = zlook;
1940	      for (cmove = ceoflen - 1, zmove = zlook;
1941		   cmove > 0;
1942		   cmove--, zmove++)
1943		zmove[0] = zmove[1];
1944
1945	      --ceofhave;
1946	    }
1947	}
1948    }
1949
1950  ubuffree (zlook);
1951
1952  if (! fsysdep_sync (e, zto))
1953    {
1954      (void) ffileclose (e);
1955      ferr = TRUE;
1956    }
1957  else
1958    {
1959      if (! ffileclose (e))
1960	ferr = TRUE;
1961    }
1962  if (ferr)
1963    ucuputs ("[file write error]");
1964
1965  if (! fsysdep_cu_copy (TRUE)
1966      || ! fsysdep_terminal_signals (FALSE))
1967    ucuabort ();
1968
1969  ucuputs (abCuconnected);
1970
1971  ubuffree (zto);
1972
1973  return UUCONF_CMDTABRET_CONTINUE;
1974}
1975
1976/* Send a buffer to the remote system.  If fCuvar_binary is FALSE,
1977   each buffer passed in will be a single line; in this case we can
1978   check the echoed characters and kill the line if they do not match.
1979   This returns FALSE if an echo check fails.  If a port error
1980   occurrs, it calls ucuabort.  */
1981
1982static boolean
1983fcusend_buf (qconn, zbufarg, cbufarg)
1984     struct sconnection *qconn;
1985     const char *zbufarg;
1986     size_t cbufarg;
1987{
1988  const char *zbuf;
1989  size_t cbuf;
1990  int ctries;
1991  size_t cbplen;
1992  char *zsendbuf;
1993
1994  zbuf = zbufarg;
1995  cbuf = cbufarg;
1996  ctries = 0;
1997
1998  if (fCuvar_binary)
1999    cbplen = strlen (zCuvar_binary_prefix);
2000  else
2001    cbplen = 1;
2002  zsendbuf = zbufalc (64 * (cbplen + 1));
2003
2004  /* Loop while we still have characters to send.  The value of cbuf
2005     will be reset to cbufarg if an echo failure occurs while sending
2006     a line in non-binary mode.  */
2007  while (cbuf > 0)
2008    {
2009      int csend;
2010      char *zput;
2011      const char *zget;
2012      boolean fnl;
2013      int i;
2014
2015      if (FGOT_SIGNAL ())
2016	{
2017	  /* Make sure the signal is logged.  */
2018	  ubuffree (zsendbuf);
2019	  ulog (LOG_ERROR, (const char *) NULL);
2020	  ucuputs ("[file send aborted]");
2021	  /* Reset the SIGINT flag so that it does not confuse us in
2022	     the future.  */
2023	  afSignal[INDEXSIG_SIGINT] = FALSE;
2024	  return FALSE;
2025	}
2026
2027      /* Discard anything we've read from the port up to now, to avoid
2028	 confusing the echo checking.  */
2029      iPrecstart = 0;
2030      iPrecend = 0;
2031
2032      /* Send all characters up to a newline before actually sending
2033	 the newline.  This makes it easier to handle the special
2034	 newline echo checking.  Send up to 64 characters at a time
2035	 before doing echo checking.  */
2036      if (*zbuf == '\n')
2037	csend = 1;
2038      else
2039	{
2040	  const char *znl;
2041
2042	  znl = memchr (zbuf, '\n', cbuf);
2043	  if (znl == NULL)
2044	    csend = cbuf;
2045	  else
2046	    csend = znl - zbuf;
2047	  if (csend > 64)
2048	    csend = 64;
2049	}
2050
2051      /* Translate this part of the buffer.  If we are not in binary
2052	 mode, we translate \n to \r, and ignore any nonprintable
2053	 characters.  */
2054      zput = zsendbuf;
2055      fnl = FALSE;
2056      for (i = 0, zget = zbuf; i < csend; i++, zget++)
2057	{
2058	  if (isprint (*zget)
2059	      || *zget == '\t')
2060	    *zput++ = *zget;
2061	  else if (*zget == '\n')
2062	    {
2063	      if (fCuvar_binary)
2064		*zput++ = '\n';
2065	      else
2066		*zput++ = '\r';
2067	      fnl = TRUE;
2068	    }
2069	  else if (fCuvar_binary)
2070	    {
2071	      strcpy (zput, zCuvar_binary_prefix);
2072	      zput += cbplen;
2073	      *zput++ = *zget;
2074	    }
2075	}
2076
2077      zbuf += csend;
2078      cbuf -= csend;
2079
2080      if (zput == zsendbuf)
2081	continue;
2082
2083      /* Send the data over the port.  */
2084      if (! fsend_data (qconn, zsendbuf, (size_t) (zput - zsendbuf), TRUE))
2085	ucuabort ();
2086
2087      /* We do echo checking if requested, unless we are in binary
2088	 mode.  Echo checking of a newline is different from checking
2089	 of normal characters; when we send a newline we look for
2090	 *zCuvar_echonl.  */
2091      if ((fCuvar_echocheck && ! fCuvar_binary)
2092	  || (fnl && *zCuvar_echonl != '\0'))
2093	{
2094	  long iend;
2095
2096	  iend = ixsysdep_time ((long *) NULL) + (long) cCuvar_timeout;
2097	  for (zget = zsendbuf; zget < zput; zget++)
2098	    {
2099	      int bread;
2100	      int bwant;
2101
2102	      if (fCuvar_binary ? *zget == '\n' : *zget == '\r')
2103		{
2104		  bwant = *zCuvar_echonl;
2105		  if (bwant == '\0')
2106		    continue;
2107		}
2108	      else
2109		{
2110		  if (! fCuvar_echocheck || ! isprint (*zget))
2111		    continue;
2112		  bwant = *zget;
2113		}
2114
2115	      do
2116		{
2117		  if (FGOT_SIGNAL ())
2118		    {
2119		      /* Make sure the signal is logged.  */
2120		      ubuffree (zsendbuf);
2121		      ulog (LOG_ERROR, (const char *) NULL);
2122		      ucuputs ("[file send aborted]");
2123		      /* Reset the SIGINT flag so that it does not
2124			 confuse us in the future.  */
2125		      afSignal[INDEXSIG_SIGINT] = FALSE;
2126		      return FALSE;
2127		    }
2128
2129		  bread = breceive_char (qconn,
2130					 iend - ixsysdep_time ((long *) NULL),
2131					 TRUE);
2132		  if (bread < 0)
2133		    {
2134		      if (bread == -2)
2135			ucuabort ();
2136
2137		      /* If we timed out, and we're not in binary
2138			 mode, we kill the line and try sending it
2139			 again from the beginning.  */
2140		      if (! fCuvar_binary && *zCuvar_kill != '\0')
2141			{
2142			  ++ctries;
2143			  if (ctries < cCuvar_resend)
2144			    {
2145			      if (fCuvar_verbose)
2146				{
2147				  printf ("R ");
2148				  (void) fflush (stdout);
2149				}
2150			      if (! fsend_data (qconn, zCuvar_kill, 1,
2151						TRUE))
2152				ucuabort ();
2153			      zbuf = zbufarg;
2154			      cbuf = cbufarg;
2155			      break;
2156			    }
2157			}
2158		      ubuffree (zsendbuf);
2159		      ucuputs ("[timed out looking for echo]");
2160		      return FALSE;
2161		    }
2162		}
2163	      while (bread != *zget);
2164
2165	      if (bread < 0)
2166		break;
2167	    }
2168	}
2169    }
2170
2171  ubuffree (zsendbuf);
2172
2173  return TRUE;
2174}
2175