Merge branch 'vendor/TCPDUMP'
[dragonfly.git] / usr.bin / tip / libacu / unidialer.c
1 /*
2  * Copyright (c) 1986, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)unidialer.c      8.1 (Berkeley) 6/6/93
34  * $FreeBSD: src/usr.bin/tip/libacu/unidialer.c,v 1.7 1999/08/28 01:06:30 peter Exp $
35  */
36
37 /*
38  * Generalized routines for calling up on a Hayes AT command set based modem.
39  * Control variables are pulled out of a modem caps-style database to
40  * configure the driver for a particular modem.
41  */
42 #include "tipconf.h"
43 #include "tip.h"
44 #include "pathnames.h"
45
46 #include <sys/times.h>
47 #include <assert.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51
52 #include "acucommon.h"
53 #include "tod.h"
54
55 /* #define DEBUG */
56 #define MAXRETRY        5
57
58 typedef enum
59 {
60         mpt_notype, mpt_string, mpt_number, mpt_boolean
61 } modem_parm_type_t;
62
63 typedef struct {
64         modem_parm_type_t modem_parm_type;
65         const char *name;
66         union {
67                 char **string;
68                 unsigned int *number;
69         } value;
70         union {
71                 char *string;
72                 unsigned int number;
73         } default_value;
74 } modem_parm_t;
75
76 /*
77         Configuration
78 */
79 static char modem_name [80];
80 static char *dial_command;
81 static char *hangup_command;
82 static char *echo_off_command;
83 static char *reset_command;
84 static char *init_string;
85 static char *escape_sequence;
86 static int hw_flow_control;
87 static int lock_baud;
88 static unsigned int intercharacter_delay;
89 static unsigned int intercommand_delay;
90 static unsigned int escape_guard_time;
91 static unsigned int reset_delay;
92
93 static int unidialer_dialer (char *num, char *acu);
94 static void unidialer_disconnect ();
95 static void unidialer_abort ();
96 static int unidialer_connect(void);
97 static int unidialer_swallow(char *);
98
99 static acu_t unidialer =
100 {
101         modem_name,
102         unidialer_dialer,
103         unidialer_disconnect,
104         unidialer_abort
105 };
106
107 /*
108         Table of parameters kept in modem database
109 */
110 modem_parm_t modem_parms [] = {
111         { mpt_string, "dial_command", &dial_command, "ATDT%s\r" },
112         { mpt_string, "hangup_command", &hangup_command, "ATH\r", },
113         { mpt_string, "echo_off_command", &echo_off_command, "ATE0\r" },
114         { mpt_string, "reset_command", &reset_command, "ATZ\r" },
115         { mpt_string, "init_string", &init_string, "AT&F\r", },
116         { mpt_string, "escape_sequence", &escape_sequence, "+++" },
117         { mpt_boolean, "hw_flow_control", (char **)&hw_flow_control, NULL },
118         { mpt_boolean, "lock_baud", (char **)&lock_baud, NULL },
119         { mpt_number, "intercharacter_delay", (char **)&intercharacter_delay, (char *)50 },
120         { mpt_number, "intercommand_delay", (char **)&intercommand_delay, (char *)300 },
121         { mpt_number, "escape_guard_time", (char **)&escape_guard_time, (char *)300 },
122         { mpt_number, "reset_delay", (char **)&reset_delay, (char *)3000 },
123         { mpt_notype, NULL, NULL, NULL }
124 };
125
126 /*
127         Forward declarations
128 */
129 static void unidialer_verbose_read ();
130 static void unidialer_modem_cmd (int fd, CONST char *cmd);
131 static void unidialer_write (int fd, CONST char *cp, int n);
132 static void unidialer_write_str (int fd, CONST char *cp);
133 static void unidialer_disconnect ();
134 static void sigALRM ();
135 static int unidialersync ();
136 static int unidialer_swallow (char *match);
137
138 /*
139         Global vars
140 */
141 static int timeout = 0;
142 static int connected = 0;
143 static jmp_buf timeoutbuf, intbuf;
144
145 #define cgetflag(f)     (cgetcap(bp, f, ':') != NULL)
146
147 #ifdef DEBUG
148
149 #define print_str(x) printf (#x " = %s\n", x)
150 #define print_num(x) printf (#x " = %d\n", x)
151
152 void dumpmodemparms (char *modem)
153 {
154                 printf ("modem parms for %s\n", modem);
155                 print_str (dial_command);
156                 print_str (hangup_command);
157                 print_str (echo_off_command);
158                 print_str (reset_command);
159                 print_str (init_string);
160                 print_str (escape_sequence);
161                 print_num (lock_baud);
162                 print_num (intercharacter_delay);
163                 print_num (intercommand_delay);
164                 print_num (escape_guard_time);
165                 print_num (reset_delay);
166                 printf ("\n");
167 }
168 #endif
169
170 static int getmodemparms (const char *modem)
171 {
172         char *bp, *db_array [3], *modempath;
173         int ndx, stat;
174         modem_parm_t *mpp;
175
176         modempath = getenv ("MODEMS");
177
178         ndx = 0;
179
180         if (modempath != NULL)
181                 db_array [ndx++] = modempath;
182
183         db_array [ndx++] = _PATH_MODEMS;
184         db_array [ndx] = NULL;
185
186         if ((stat = cgetent (&bp, db_array, (char *)modem)) < 0) {
187                 switch (stat) {
188                 case -1:
189                         warnx ("unknown modem %s", modem);
190                         break;
191                 case -2:
192                         warnx ("can't open modem description file");
193                         break;
194                 case -3:
195                         warnx ("possible reference loop in modem description file");
196                         break;
197                 }
198                 return 0;
199         }
200         for (mpp = modem_parms; mpp->name; mpp++)
201         {
202                 switch (mpp->modem_parm_type)
203                 {
204                         case mpt_string:
205                                 if (cgetstr (bp, (char *)mpp->name, mpp->value.string) == -1)
206                                         *mpp->value.string = mpp->default_value.string;
207                                 break;
208
209                         case mpt_number:
210                         {
211                                 long l;
212                                 if (cgetnum (bp, (char *)mpp->name, &l) == -1)
213                                         *mpp->value.number = mpp->default_value.number;
214                                 else
215                                         *mpp->value.number = (unsigned int)l;
216                         }
217                                 break;
218
219                         case mpt_boolean:
220                                 *mpp->value.number = cgetflag ((char *)mpp->name);
221                                 break;
222                 }
223         }
224         strncpy (modem_name, modem, sizeof (modem_name) - 1);
225         modem_name [sizeof (modem_name) - 1] = '\0';
226         return 1;
227 }
228
229 /*
230 */
231 acu_t* unidialer_getmodem (const char *modem_name)
232 {
233         acu_t* rc = NULL;
234         if (getmodemparms (modem_name))
235                 rc = &unidialer;
236         return rc;
237 }
238
239 static int unidialer_modem_ready ()
240 {
241 #ifdef TIOCMGET
242         int state;
243         ioctl (FD, TIOCMGET, &state);
244         return (state & TIOCM_DSR) ? 1 : 0;
245 #else
246         return (1);
247 #endif
248 }
249
250 static int unidialer_waitfor_modem_ready (int ms)
251 {
252 #ifdef TIOCMGET
253         int count;
254         for (count = 0; count < ms; count += 100)
255         {
256                 if (unidialer_modem_ready ())
257                 {
258 #ifdef DEBUG
259                         printf ("unidialer_waitfor_modem_ready: modem ready.\n");
260 #endif
261                         break;
262                 }
263                 acu_nap (100);
264         }
265         return (count < ms);
266 #else
267         acu_nap (250);
268         return (1);
269 #endif
270 }
271
272 int unidialer_tty_clocal (int flag)
273 {
274 #if HAVE_TERMIOS
275         struct termios t;
276         tcgetattr (FD, &t);
277         if (flag)
278                 t.c_cflag |= CLOCAL;
279         else
280                 t.c_cflag &= ~CLOCAL;
281         tcsetattr (FD, TCSANOW, &t);
282 #elif defined(TIOCMSET)
283         int state;
284         /*
285                 Don't have CLOCAL so raise CD in software to
286                 get the same effect.
287         */
288         ioctl (FD, TIOCMGET, &state);
289         if (flag)
290                 state |= TIOCM_CD;
291         else
292                 state &= ~TIOCM_CD;
293         ioctl (FD, TIOCMSET, &state);
294 #endif
295 }
296
297 int unidialer_get_modem_response (char *buf, int bufsz, int response_timeout)
298 {
299         sig_t f;
300         char c, *p = buf, *lid = buf + bufsz - 1;
301         int state;
302
303         assert (bufsz > 0);
304
305         f = signal (SIGALRM, sigALRM);
306
307         timeout = 0;
308
309         if (setjmp (timeoutbuf)) {
310                 signal (SIGALRM, f);
311                 *p = '\0';
312 #ifdef DEBUG
313                 printf ("get_response: timeout buf=%s, state=%d\n", buf, state);
314 #endif
315                 return (0);
316         }
317
318         ualarm (response_timeout * 1000, 0);
319
320         state = 0;
321
322         while (1)
323         {
324                 switch (state)
325                 {
326                         case 0:
327                                 if (read (FD, &c, 1) == 1)
328                                 {
329                                         if (c == '\r')
330                                         {
331                                                 ++state;
332                                         }
333                                         else
334                                         {
335 #ifdef DEBUG
336                                                 printf ("get_response: unexpected char %s.\n", ctrl (c));
337 #endif
338                                         }
339                                 }
340                                 break;
341
342                         case 1:
343                                 if (read (FD, &c, 1) == 1)
344                                 {
345                                         if (c == '\n')
346                                         {
347 #ifdef DEBUG
348                                                 printf ("get_response: <CRLF> encountered.\n", buf);
349 #endif
350                                                 ++state;
351                                         }
352                                         else
353                                         {
354                                                         state = 0;
355 #ifdef DEBUG
356                                                         printf ("get_response: unexpected char %s.\n", ctrl (c));
357 #endif
358                                         }
359                                 }
360                                 break;
361
362                         case 2:
363                                 if (read (FD, &c, 1) == 1)
364                                 {
365                                         if (c == '\r')
366                                                 ++state;
367                                         else if (c >= ' ' && p < lid)
368                                                 *p++ = c;
369                                 }
370                                 break;
371
372                         case 3:
373                                 if (read (FD, &c, 1) == 1)
374                                 {
375                                         if (c == '\n')
376                                         {
377                                                 signal (SIGALRM, f);
378                                                 /* ualarm (0, 0); */
379                                                 alarm (0);
380                                                 *p = '\0';
381 #ifdef DEBUG
382                                                 printf ("get_response: %s\n", buf);
383 #endif
384                                                 return (1);
385                                         }
386                                         else
387                                         {
388                                                 state = 0;
389                                                 p = buf;
390                                         }
391                                 }
392                                 break;
393                 }
394         }
395 }
396
397 int unidialer_get_okay (int ms)
398 {
399         int okay;
400         char buf [BUFSIZ];
401         okay = unidialer_get_modem_response (buf, sizeof (buf), ms) &&
402                 strcmp (buf, "OK") == 0;
403         return okay;
404 }
405
406 static int unidialer_dialer (char *num, char *acu)
407 {
408         char *cp;
409         char dial_string [80];
410 #if ACULOG
411         char line [80];
412 #endif
413
414         #ifdef DEBUG
415         dumpmodemparms (modem_name);
416         #endif
417
418         if (lock_baud) {
419                 int i;
420                 if ((i = speed(number(value(BAUDRATE)))) == 0)
421                         return 0;
422                 ttysetup (i);
423         }
424
425         if (boolean(value(VERBOSE)))
426                 printf("Using \"%s\"\n", acu);
427
428         acu_hupcl ();
429
430         /*
431          * Get in synch.
432          */
433         if (!unidialersync()) {
434 badsynch:
435                 printf("tip: can't synchronize with %s\n", modem_name);
436 #if ACULOG
437                 logent(value(HOST), num, modem_name, "can't synch up");
438 #endif
439                 return (0);
440         }
441
442         unidialer_modem_cmd (FD, echo_off_command);     /* turn off echoing */
443
444         sleep(1);
445
446 #ifdef DEBUG
447         if (boolean(value(VERBOSE)))
448                 unidialer_verbose_read();
449 #endif
450
451         acu_flush (); /* flush any clutter */
452
453         unidialer_modem_cmd (FD, init_string);
454
455         if (!unidialer_get_okay (reset_delay))
456                 goto badsynch;
457
458         fflush (stdout);
459
460         for (cp = num; *cp; cp++)
461                 if (*cp == '=')
462                         *cp = ',';
463
464         (void) sprintf (dial_string, dial_command, num);
465
466         unidialer_modem_cmd (FD, dial_string);
467
468         connected = unidialer_connect ();
469
470         if (connected && hw_flow_control) {
471                 acu_hw_flow_control (hw_flow_control);
472         }
473
474 #if ACULOG
475         if (timeout) {
476                 sprintf(line, "%d second dial timeout",
477                         number(value(DIALTIMEOUT)));
478                 logent(value(HOST), num, modem_name, line);
479         }
480 #endif
481
482         if (timeout)
483                 unidialer_disconnect ();
484
485         return (connected);
486 }
487
488 static void unidialer_disconnect ()
489 {
490         int okay, retries;
491
492         acu_flush (); /* flush any clutter */
493
494         unidialer_tty_clocal (TRUE);
495
496         /* first hang up the modem*/
497         ioctl (FD, TIOCCDTR, 0);
498         acu_nap (250);
499         ioctl (FD, TIOCSDTR, 0);
500
501         /*
502          * If AT&D2, then dropping DTR *should* just hangup the modem. But
503          * some modems reset anyway; also, the modem may be programmed to reset
504          * anyway with AT&D3. Play it safe and wait for the full reset time before
505          * proceeding.
506          */
507         acu_nap (reset_delay);
508
509         if (!unidialer_waitfor_modem_ready (reset_delay))
510         {
511 #ifdef DEBUG
512                         printf ("unidialer_disconnect: warning CTS low.\r\n");
513 #endif
514         }
515
516         /*
517          * If not strapped for DTR control, try to get command mode.
518          */
519         for (retries = okay = 0; retries < MAXRETRY && !okay; retries++)
520         {
521                 int timeout_value;
522                 /* flush any clutter */
523                 if (!acu_flush ())
524                 {
525 #ifdef DEBUG
526                         printf ("unidialer_disconnect: warning flush failed.\r\n");
527 #endif
528                 }
529                 timeout_value = escape_guard_time;
530                 timeout_value += (timeout_value * retries / MAXRETRY);
531                 acu_nap (timeout_value);
532                 acu_flush (); /* flush any clutter */
533                 unidialer_modem_cmd (FD, escape_sequence);
534                 acu_nap (timeout_value);
535                 unidialer_modem_cmd (FD, hangup_command);
536                 okay = unidialer_get_okay (reset_delay);
537         }
538         if (!okay)
539         {
540                 #if ACULOG
541                 logent(value(HOST), "", modem_name, "can't hang up modem");
542                 #endif
543                 if (boolean(value(VERBOSE)))
544                         printf("hang up failed\n");
545         }
546         (void) acu_flush ();
547         close (FD);
548 }
549
550 static void unidialer_abort ()
551 {
552         unidialer_write_str (FD, "\r"); /* send anything to abort the call */
553         unidialer_disconnect ();
554 }
555
556 static void sigALRM ()
557 {
558         (void) printf("\07timeout waiting for reply\n");
559         timeout = 1;
560         longjmp(timeoutbuf, 1);
561 }
562
563 static int unidialer_swallow (char *match)
564 {
565         sig_t f;
566         char c;
567
568         f = signal(SIGALRM, sigALRM);
569
570         timeout = 0;
571
572         if (setjmp(timeoutbuf)) {
573                 signal(SIGALRM, f);
574                 return (0);
575         }
576
577         alarm(number(value(DIALTIMEOUT)));
578
579         do {
580                 if (*match =='\0') {
581                         signal(SIGALRM, f);
582                         alarm (0);
583                         return (1);
584                 }
585                 do {
586                         read (FD, &c, 1);
587                 } while (c == '\0');
588                 c &= 0177;
589 #ifdef DEBUG
590                 if (boolean(value(VERBOSE)))
591                 {
592                         /* putchar(c); */
593                         printf (ctrl (c));
594                 }
595 #endif
596         } while (c == *match++);
597         signal(SIGALRM, SIG_DFL);
598         alarm(0);
599 #ifdef DEBUG
600         if (boolean(value(VERBOSE)))
601                 fflush (stdout);
602 #endif
603         return (0);
604 }
605
606 static struct baud_msg {
607         char *msg;
608         int baud;
609 } baud_msg[] = {
610         "",             B300,
611         " 1200",        B1200,
612         " 2400",        B2400,
613         " 9600",        B9600,
614         " 9600/ARQ",    B9600,
615         0,              0,
616 };
617
618 static int unidialer_connect ()
619 {
620         char c;
621         int nc, nl, n;
622         char dialer_buf[64];
623         struct baud_msg *bm;
624         sig_t f;
625
626         if (unidialer_swallow("\r\n") == 0)
627                 return (0);
628         f = signal(SIGALRM, sigALRM);
629 again:
630         nc = 0; nl = sizeof(dialer_buf)-1;
631         bzero(dialer_buf, sizeof(dialer_buf));
632         timeout = 0;
633         for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
634                 if (setjmp(timeoutbuf))
635                         break;
636                 alarm(number(value(DIALTIMEOUT)));
637                 n = read(FD, &c, 1);
638                 alarm(0);
639                 if (n <= 0)
640                         break;
641                 c &= 0x7f;
642                 if (c == '\r') {
643                         if (unidialer_swallow("\n") == 0)
644                                 break;
645                         if (!dialer_buf[0])
646                                 goto again;
647                         if (strcmp(dialer_buf, "RINGING") == 0 &&
648                             boolean(value(VERBOSE))) {
649 #ifdef DEBUG
650                                 printf("%s\r\n", dialer_buf);
651 #endif
652                                 goto again;
653                         }
654                         if (strncmp(dialer_buf, "CONNECT",
655                                     sizeof("CONNECT")-1) != 0)
656                                 break;
657                         if (lock_baud) {
658                                 signal(SIGALRM, f);
659 #ifdef DEBUG
660                                 if (boolean(value(VERBOSE)))
661                                         printf("%s\r\n", dialer_buf);
662 #endif
663                                 return (1);
664                         }
665                         for (bm = baud_msg ; bm->msg ; bm++)
666                                 if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) {
667                                         if (!acu_setspeed (bm->baud))
668                                                 goto error;
669                                         signal(SIGALRM, f);
670 #ifdef DEBUG
671                                         if (boolean(value(VERBOSE)))
672                                                 printf("%s\r\n", dialer_buf);
673 #endif
674                                         return (1);
675                                 }
676                         break;
677                 }
678                 dialer_buf[nc] = c;
679         }
680 error1:
681         printf("%s\r\n", dialer_buf);
682 error:
683         signal(SIGALRM, f);
684         return (0);
685 }
686
687 /*
688  * This convoluted piece of code attempts to get
689  * the unidialer in sync.
690  */
691 static int unidialersync ()
692 {
693         int already = 0;
694         int len;
695         char buf[40];
696
697         while (already++ < MAXRETRY) {
698                 acu_nap (intercommand_delay);
699                 acu_flush (); /* flush any clutter */
700                 unidialer_write_str (FD, reset_command); /* reset modem */
701                 bzero(buf, sizeof(buf));
702                 acu_nap (reset_delay);
703                 ioctl (FD, FIONREAD, &len);
704                 if (len) {
705                         len = read(FD, buf, sizeof(buf));
706 #ifdef DEBUG
707                         buf [len] = '\0';
708                         printf("unidialersync (%s): (\"%s\")\n\r", modem_name, buf);
709 #endif
710                         if (index(buf, '0') ||
711                            (index(buf, 'O') && index(buf, 'K')))
712                                 return(1);
713                 }
714                 /*
715                  * If not strapped for DTR control,
716                  * try to get command mode.
717                  */
718                 acu_nap (escape_guard_time);
719                 unidialer_write_str (FD, escape_sequence);
720                 acu_nap (escape_guard_time);
721                 unidialer_write_str (FD, hangup_command);
722                 /*
723                  * Toggle DTR to force anyone off that might have left
724                  * the modem connected.
725                  */
726                 acu_nap (escape_guard_time);
727                 ioctl (FD, TIOCCDTR, 0);
728                 acu_nap (1000);
729                 ioctl (FD, TIOCSDTR, 0);
730         }
731         acu_nap (intercommand_delay);
732         unidialer_write_str (FD, reset_command);
733         return (0);
734 }
735
736 /*
737         Send commands to modem; impose delay between commands.
738 */
739 static void unidialer_modem_cmd (int fd, const char *cmd)
740 {
741         static struct timeval oldt = { 0, 0 };
742         struct timeval newt;
743         tod_gettime (&newt);
744         if (tod_lt (&newt, &oldt))
745         {
746                 unsigned int naptime;
747                 tod_subfrom (&oldt, newt);
748                 naptime = oldt.tv_sec * 1000 + oldt.tv_usec / 1000;
749                 if (naptime > intercommand_delay)
750                 {
751 #ifdef DEBUG
752                 printf ("unidialer_modem_cmd: suspicious naptime (%u ms)\r\n", naptime);
753 #endif
754                         naptime = intercommand_delay;
755                 }
756 #ifdef DEBUG
757                 printf ("unidialer_modem_cmd: delaying %u ms\r\n", naptime);
758 #endif
759                 acu_nap (naptime);
760         }
761         unidialer_write_str (fd, cmd);
762         tod_gettime (&oldt);
763         newt.tv_sec = 0;
764         newt.tv_usec = intercommand_delay;
765         tod_addto (&oldt, &newt);
766 }
767
768 static void unidialer_write_str (int fd, const char *cp)
769 {
770 #ifdef DEBUG
771         printf ("unidialer (%s): sending %s\n", modem_name, cp);
772 #endif
773         unidialer_write (fd, cp, strlen (cp));
774 }
775
776 static void unidialer_write (int fd, const char *cp, int n)
777 {
778         acu_nap (intercharacter_delay);
779         for ( ; n-- ; cp++) {
780                 write (fd, cp, 1);
781                 acu_nap (intercharacter_delay);
782         }
783 }
784
785 #ifdef DEBUG
786 static void unidialer_verbose_read()
787 {
788         int n = 0;
789         char buf[BUFSIZ];
790
791         if (ioctl(FD, FIONREAD, &n) < 0)
792                 return;
793         if (n <= 0)
794                 return;
795         if (read(FD, buf, n) != n)
796                 return;
797         write(1, buf, n);
798 }
799 #endif
800
801 /* end of unidialer.c */