xref: /openbsd-src/usr.bin/tftp/main.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: main.c,v 1.45 2022/10/04 08:03:26 kn Exp $	*/
2 /*	$NetBSD: main.c,v 1.6 1995/05/21 16:54:10 mycroft Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * TFTP User Program -- Command Interface
35  *
36  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
37  */
38 
39 #include <sys/socket.h>
40 
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <arpa/tftp.h>
44 
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <netdb.h>
50 #include <poll.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <limits.h>
57 
58 #include "extern.h"
59 
60 #define	LBUFLEN		200		/* size of input buffer */
61 #define	MAXARGV		20
62 #define HELPINDENT	(sizeof("connect"))
63 
64 void			 get(int, char **);
65 void			 help(int, char **);
66 void			 modecmd(int, char **);
67 void			 put(int, char **);
68 void			 quit(int, char **);
69 void			 setascii(int, char **);
70 void			 setbinary(int, char **);
71 void			 setpeer(char *, char *);
72 void			 parsearg(int, char **);
73 void			 setrexmt(int, char **);
74 void			 settimeout(int, char **);
75 void			 settrace(int, char **);
76 void			 setverbose(int, char **);
77 void			 settsize(int, char **);
78 void			 settout(int, char **);
79 void			 setblksize(int, char **);
80 void			 status(int, char **);
81 int			 readcmd(char *, int, FILE *);
82 static void		 getusage(char *);
83 static int		 makeargv(void);
84 static void		 putusage(char *);
85 static void		 settftpmode(char *);
86 static __dead void	 command(void);
87 struct cmd	 	*getcmd(char *);
88 char			*tail(char *);
89 
90 struct sockaddr_storage	 peeraddr;
91 int			 f;
92 int			 trace;
93 int			 verbose;
94 int			 connected;
95 char			 mode[32];
96 char			 line[LBUFLEN];
97 int			 margc;
98 char			*margv[MAXARGV+1];
99 char			*prompt = "tftp";
100 void			 intr(int);
101 int	 		 rexmtval = TIMEOUT;
102 int	 		 maxtimeout = 5 * TIMEOUT;
103 char	 		 hostname[HOST_NAME_MAX+1];
104 FILE			*file = NULL;
105 volatile sig_atomic_t	 intrflag = 0;
106 char			*ackbuf;
107 int			 has_options = 0;
108 int			 opt_tsize = 0;
109 int			 opt_tout = 0;
110 int			 opt_blksize = 0;
111 
112 struct cmd {
113 	char	*name;
114 	char	*help;
115 	void	 (*handler)(int, char **);
116 };
117 
118 struct cmd cmdtab[] = {
119 	{ "?",		"print help information",	help },
120 	{ "ascii",	"set mode to netascii",	setascii },
121 	{ "binary",	"set mode to octet",	setbinary },
122 	{ "blksize",	"set alternative blksize option",	setblksize },
123 	{ "connect",	"connect to remote tftp",	parsearg },
124 	{ "get",	"receive file",	get },
125 	{ "help",	"print help information",	help },
126 	{ "mode",       "set file transfer mode",	modecmd },
127 	{ "put",	"send file",	put },
128 	{ "quit",	"exit tftp",	quit },
129 	{ "rexmt",	"set per-packet retransmission timeout", setrexmt },
130 	{ "status",	"show current status",	status },
131 	{ "timeout",	"set total retransmission timeout",	settimeout },
132 	{ "tout",	"toggle timeout option",	settout },
133 	{ "trace",	"toggle packet tracing",	settrace },
134 	{ "tsize",	"toggle tsize option",	settsize },
135 	{ "verbose",	"toggle verbose mode",	setverbose },
136 	{ NULL,		NULL,	NULL }
137 };
138 
139 struct	modes {
140 	char	*m_name;
141 	char	*m_mode;
142 } modes[] = {
143 	{ "ascii",	"netascii" },
144 	{ "netascii",	"netascii" },
145 	{ "binary",	"octet" },
146 	{ "image",	"octet" },
147 	{ "octet",	"octet" },
148 /*	{ "mail",	"mail" }, */
149 	{ NULL,		NULL }
150 };
151 
152 int
153 main(int argc, char *argv[])
154 {
155 	f = -1;
156 
157 	if (pledge("stdio rpath wpath cpath dns inet", NULL) == -1)
158 		err(1, "pledge");
159 
160 	/* set default transfer mode */
161 	strlcpy(mode, "netascii", sizeof(mode));
162 
163 	/* set peer if given */
164 	if (argc > 1)
165 		parsearg(argc, argv);
166 
167 	/* catch SIGINT */
168 	signal(SIGINT, intr);
169 
170 	/* allocate memory for packets */
171 	if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL)
172 		err(1, "malloc");
173 
174 	/* command prompt */
175 	command();
176 
177 	return (0);
178 }
179 
180 void
181 setpeer(char *host, char *port)
182 {
183 	struct addrinfo hints, *res0, *res;
184 	int error;
185 	struct sockaddr_storage ss;
186 	char *cause = "unknown";
187 
188 	if (connected) {
189 		close(f);
190 		f = -1;
191 	}
192 	connected = 0;
193 
194 	memset(&hints, 0, sizeof(hints));
195 	hints.ai_family = PF_UNSPEC;
196 	hints.ai_socktype = SOCK_DGRAM;
197 	hints.ai_protocol = IPPROTO_UDP;
198 	hints.ai_flags = AI_CANONNAME;
199 	if (!port)
200 		port = "tftp";
201 	error = getaddrinfo(host, port, &hints, &res0);
202 	if (error) {
203 		warnx("%s", gai_strerror(error));
204 		return;
205 	}
206 
207 	for (res = res0; res; res = res->ai_next) {
208 		if (res->ai_addrlen > sizeof(peeraddr))
209 			continue;
210 		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
211 		if (f < 0) {
212 			cause = "socket";
213 			continue;
214 		}
215 
216 		memset(&ss, 0, sizeof(ss));
217 		ss.ss_family = res->ai_family;
218 		if (bind(f, (struct sockaddr *)&ss, res->ai_addrlen) < 0) {
219 			cause = "bind";
220 			close(f);
221 			f = -1;
222 			continue;
223 		}
224 
225 		break;
226 	}
227 
228 	if (f < 0)
229 		warn("%s", cause);
230 	else {
231 		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
232 		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
233 		if (res->ai_canonname) {
234 			(void)strlcpy(hostname, res->ai_canonname,
235 			    sizeof(hostname));
236 		} else
237 			(void)strlcpy(hostname, host, sizeof(hostname));
238 		connected = 1;
239 	}
240 	freeaddrinfo(res0);
241 }
242 
243 void
244 parsearg(int argc, char *argv[])
245 {
246 	if (argc < 2) {
247 		strlcpy(line, "Connect ", sizeof(line));
248 		printf("(to) ");
249 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
250 		if (makeargv())
251 			return;
252 		argc = margc;
253 		argv = margv;
254 	}
255 	if ((argc < 2) || (argc > 3)) {
256 		printf("usage: %s [host [port]]\n", argv[0]);
257 		return;
258 	}
259 	if (argc == 2)
260 		setpeer(argv[1], NULL);
261 	else
262 		setpeer(argv[1], argv[2]);
263 }
264 
265 void
266 modecmd(int argc, char *argv[])
267 {
268 	struct modes	*p;
269 	char		*sep;
270 
271 	if (argc < 2) {
272 		printf("Using %s mode to transfer files.\n", mode);
273 		return;
274 	}
275 	if (argc == 2) {
276 		for (p = modes; p->m_name != NULL; p++)
277 			if (strcmp(argv[1], p->m_name) == 0)
278 				break;
279 		if (p->m_name) {
280 			settftpmode(p->m_mode);
281 			return;
282 		}
283 		printf("%s: unknown mode\n", argv[1]);
284 		/* drop through and print usage message */
285 	}
286 
287 	printf("usage: %s [", argv[0]);
288 	sep = " ";
289 	for (p = modes; p->m_name != NULL; p++) {
290 		printf("%s%s", sep, p->m_name);
291 		if (*sep == ' ')
292 			sep = " | ";
293 	}
294 	printf(" ]\n");
295 
296 	return;
297 }
298 
299 /* ARGSUSED */
300 void
301 setbinary(int argc, char *argv[])
302 {
303 	settftpmode("octet");
304 }
305 
306 /* ARGSUSED */
307 void
308 setascii(int argc, char *argv[])
309 {
310 	settftpmode("netascii");
311 }
312 
313 static void
314 settftpmode(char *newmode)
315 {
316 	strlcpy(mode, newmode, sizeof(mode));
317 	if (verbose)
318 		printf("mode set to %s\n", mode);
319 }
320 
321 /*
322  * Send file(s).
323  */
324 void
325 put(int argc, char *argv[])
326 {
327 	int	 fd;
328 	int	 n;
329 	char	*cp, *targ;
330 
331 	if (argc < 2) {
332 		strlcpy(line, "put ", sizeof(line));
333 		printf("(file) ");
334 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
335 		if (makeargv())
336 			return;
337 		argc = margc;
338 		argv = margv;
339 	}
340 	if (argc < 2) {
341 		putusage(argv[0]);
342 		return;
343 	}
344 	targ = argv[argc - 1];
345 	if (strrchr(argv[argc - 1], ':')) {
346 
347 		for (n = 1; n < argc - 1; n++)
348 			if (strchr(argv[n], ':')) {
349 				putusage(argv[0]);
350 				return;
351 			}
352 		cp = argv[argc - 1];
353 		targ = strrchr(cp, ':');
354 		*targ++ = 0;
355 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
356 			cp[strlen(cp) - 1] = '\0';
357 			cp++;
358 		}
359 		setpeer(cp, NULL);
360 	}
361 	if (!connected) {
362 		printf("No target machine specified.\n");
363 		return;
364 	}
365 	if (argc < 4) {
366 		cp = argc == 2 ? tail(targ) : argv[1];
367 		fd = open(cp, O_RDONLY);
368 		if (fd < 0) {
369 			warn("open: %s", cp);
370 			return;
371 		}
372 		if (verbose)
373 			printf("putting %s to %s:%s [%s]\n",
374 			    cp, hostname, targ, mode);
375 		sendfile(fd, targ, mode);
376 		return;
377 	}
378 
379 	/*
380 	 * this assumes the target is a directory on
381 	 * on a remote unix system.  hmmmm.
382 	 */
383 	for (n = 1; n < argc - 1; n++) {
384 		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
385 			err(1, "asprintf");
386 		fd = open(argv[n], O_RDONLY);
387 		if (fd < 0) {
388 			warn("open: %s", argv[n]);
389 			free(cp);
390 			continue;
391 		}
392 		if (verbose)
393 			printf("putting %s to %s:%s [%s]\n",
394 			    argv[n], hostname, cp, mode);
395 		sendfile(fd, cp, mode);
396 		free(cp);
397 	}
398 }
399 
400 static void
401 putusage(char *s)
402 {
403 	printf("usage: %s file [[host:]remotename]\n", s);
404 	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
405 	    s);
406 }
407 
408 /*
409  * Receive file(s).
410  */
411 void
412 get(int argc, char *argv[])
413 {
414 	int	 fd;
415 	int	 n;
416 	char	*cp;
417 	char	*src;
418 
419 	if (argc < 2) {
420 		strlcpy(line, "get ", sizeof(line));
421 		printf("(files) ");
422 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
423 		if (makeargv())
424 			return;
425 		argc = margc;
426 		argv = margv;
427 	}
428 	if (argc < 2) {
429 		getusage(argv[0]);
430 		return;
431 	}
432 	if (!connected) {
433 		for (n = 1; n < argc; n++)
434 			if (strrchr(argv[n], ':') == 0) {
435 				getusage(argv[0]);
436 				return;
437 			}
438 	}
439 	for (n = 1; n < argc; n++) {
440 		src = strrchr(argv[n], ':');
441 		if (src == NULL)
442 			src = argv[n];
443 		else {
444 			char *cp;
445 
446 			*src++ = 0;
447 			cp = argv[n];
448 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
449 				cp[strlen(cp) - 1] = '\0';
450 				cp++;
451 			}
452 			setpeer(cp, NULL);
453 			if (!connected)
454 				continue;
455 		}
456 		if (argc < 4) {
457 			cp = argc == 3 ? argv[2] : tail(src);
458 			fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
459 			if (fd < 0) {
460 				warn("create: %s", cp);
461 				return;
462 			}
463 			if (verbose)
464 				printf("getting from %s:%s to %s [%s]\n",
465 				    hostname, src, cp, mode);
466 			recvfile(fd, src, mode);
467 			break;
468 		}
469 		cp = tail(src);	/* new .. jdg */
470 		fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
471 		if (fd < 0) {
472 			warn("create: %s", cp);
473 			continue;
474 		}
475 		if (verbose)
476 			printf("getting from %s:%s to %s [%s]\n",
477 			    hostname, src, cp, mode);
478 		recvfile(fd, src, mode);
479 	}
480 }
481 
482 static void
483 getusage(char *s)
484 {
485 	printf("usage: %s [host:]file [localname]\n", s);
486 	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
487 }
488 
489 void
490 setrexmt(int argc, char *argv[])
491 {
492 	int		 t;
493 	const char	*errstr;
494 
495 	if (argc < 2) {
496 		strlcpy(line, "Rexmt-timeout ", sizeof(line));
497 		printf("(value) ");
498 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
499 		if (makeargv())
500 			return;
501 		argc = margc;
502 		argv = margv;
503 	}
504 	if (argc != 2) {
505 		printf("usage: %s value\n", argv[0]);
506 		return;
507 	}
508 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
509 	if (errstr)
510 		printf("%s: value is %s\n", argv[1], errstr);
511 	else
512 		rexmtval = t;
513 }
514 
515 void
516 settimeout(int argc, char *argv[])
517 {
518 	int		 t;
519 	const char	*errstr;
520 
521 	if (argc < 2) {
522 		strlcpy(line, "Maximum-timeout ", sizeof(line));
523 		printf("(value) ");
524 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
525 		if (makeargv())
526 			return;
527 		argc = margc;
528 		argv = margv;
529 	}
530 	if (argc != 2) {
531 		printf("usage: %s value\n", argv[0]);
532 		return;
533 	}
534 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
535 	if (errstr)
536 		printf("%s: value is %s\n", argv[1], errstr);
537 	else
538 		maxtimeout = t;
539 }
540 
541 /* ARGSUSED */
542 void
543 status(int argc, char *argv[])
544 {
545 	if (connected)
546 		printf("Connected to %s.\n", hostname);
547 	else
548 		printf("Not connected.\n");
549 	printf("Mode: %s Verbose: %s Tracing: %s\n",
550 	    mode, verbose ? "on" : "off", trace ? "on" : "off");
551 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
552 	    rexmtval, maxtimeout);
553 }
554 
555 /* ARGSUSED */
556 void
557 intr(int signo)
558 {
559 	intrflag = 1;
560 }
561 
562 char *
563 tail(char *filename)
564 {
565 	char	*s;
566 
567 	while (*filename) {
568 		s = strrchr(filename, '/');
569 		if (s == NULL)
570 			break;
571 		if (s[1])
572 			return (s + 1);
573 		*s = '\0';
574 	}
575 
576 	return (filename);
577 }
578 
579 /*
580  * Command parser.
581  */
582 static __dead void
583 command(void)
584 {
585 	struct cmd	*c;
586 
587 	for (;;) {
588 		if (isatty(STDIN_FILENO))
589 			printf("%s> ", prompt);
590 		if (readcmd(line, LBUFLEN, stdin) < 1)
591 			continue;
592 		if ((line[0] == 0) || (line[0] == '\n'))
593 			continue;
594 		if (makeargv())
595 			continue;
596 		if (margc == 0)
597 			continue;
598 		c = getcmd(margv[0]);
599 		if (c == (struct cmd *) - 1) {
600 			printf("?Ambiguous command\n");
601 			continue;
602 		}
603 		if (c == 0) {
604 			printf("?Invalid command\n");
605 			continue;
606 		}
607 		(*c->handler)(margc, margv);
608 	}
609 }
610 
611 struct cmd *
612 getcmd(char *name)
613 {
614 	char		*p, *q;
615 	struct cmd	*c, *found;
616 	int		 nmatches, longest;
617 
618 	longest = 0;
619 	nmatches = 0;
620 	found = 0;
621 	intrflag = 0;
622 	for (c = cmdtab; (p = c->name) != NULL; c++) {
623 		for (q = name; *q == *p++; q++)
624 			if (*q == 0)		/* exact match? */
625 				return (c);
626 		if (!*q) {			/* the name was a prefix */
627 			if (q - name > longest) {
628 				longest = q - name;
629 				nmatches = 1;
630 				found = c;
631 			} else if (q - name == longest)
632 				nmatches++;
633 		}
634 	}
635 	if (nmatches > 1)
636 		return ((struct cmd *) - 1);
637 
638 	return (found);
639 }
640 
641 /*
642  * Slice a string up into argc/argv.
643  */
644 static int
645 makeargv(void)
646 {
647 	char	 *cp;
648 	char	**argp = margv;
649 	int	  ret = 0;
650 
651 	margc = 0;
652 	for (cp = line; *cp;) {
653 		if (margc >= MAXARGV) {
654 			printf("too many arguments\n");
655 			ret = 1;
656 			break;
657 		}
658 		while (isspace((unsigned char)*cp))
659 			cp++;
660 		if (*cp == '\0')
661 			break;
662 		*argp++ = cp;
663 		margc += 1;
664 		while (*cp != '\0' && !isspace((unsigned char)*cp))
665 			cp++;
666 		if (*cp == '\0')
667 			break;
668 		*cp++ = '\0';
669 	}
670 	*argp++ = 0;
671 
672 	return (ret);
673 }
674 
675 /* ARGSUSED */
676 void
677 quit(int argc, char *argv[])
678 {
679 	exit(0);
680 }
681 
682 /*
683  * Help command.
684  */
685 void
686 help(int argc, char *argv[])
687 {
688 	struct cmd	*c;
689 
690 	if (argc == 1) {
691 		printf("Commands may be abbreviated.  Commands are:\n\n");
692 		for (c = cmdtab; c->name != NULL; c++)
693 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
694 		return;
695 	}
696 	while (--argc > 0) {
697 		char *arg;
698 		arg = *++argv;
699 		c = getcmd(arg);
700 		if (c == (struct cmd *) - 1)
701 			printf("?Ambiguous help command %s\n", arg);
702 		else if (c == NULL)
703 			printf("?Invalid help command %s\n", arg);
704 		else
705 			printf("%s\n", c->help);
706 	}
707 }
708 
709 /* ARGSUSED */
710 void
711 settrace(int argc, char *argv[])
712 {
713 	trace = !trace;
714 	printf("Packet tracing %s.\n", trace ? "on" : "off");
715 }
716 
717 /* ARGSUSED */
718 void
719 setverbose(int argc, char *argv[])
720 {
721 	verbose = !verbose;
722 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
723 }
724 
725 /* ARGSUSED */
726 void
727 settsize(int argc, char *argv[])
728 {
729 	opt_tsize = !opt_tsize;
730 	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
731 	if (opt_tsize)
732 		has_options++;
733 	else
734 		has_options--;
735 }
736 
737 /* ARGSUSED */
738 void
739 settout(int argc, char *argv[])
740 {
741 	opt_tout = !opt_tout;
742 	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
743 	if (opt_tout)
744 		has_options++;
745 	else
746 		has_options--;
747 }
748 
749 void
750 setblksize(int argc, char *argv[])
751 {
752 	int		 t;
753 	const char	*errstr;
754 
755 	if (argc < 2) {
756 		strlcpy(line, "Blocksize ", sizeof(line));
757 		printf("(value) ");
758 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
759 		if (makeargv())
760 			return;
761 		argc = margc;
762 		argv = margv;
763 	}
764 	if (argc != 2) {
765 		printf("usage: %s value\n", argv[0]);
766 		return;
767 	}
768 	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
769 	if (errstr)
770 		printf("%s: value is %s\n", argv[1], errstr);
771 	else {
772 		if (opt_blksize == 0)
773 			has_options++;
774 		opt_blksize = t;
775 	}
776 }
777 
778 int
779 readcmd(char *input, int len, FILE *stream)
780 {
781 	int		nfds;
782 	struct pollfd	pfd[1];
783 
784 	fflush(stdout);
785 
786 	pfd[0].fd = 0;
787 	pfd[0].events = POLLIN;
788 	nfds = poll(pfd, 1, INFTIM);
789 	if (nfds == -1) {
790 		if (intrflag) {
791 			intrflag = 0;
792 			putchar('\n');
793 			return (0);
794 		}
795 		exit(1);
796 	}
797 
798 	if (fgets(input, len, stream) == NULL) {
799 		if (feof(stdin))
800 			exit(0);
801 		else
802 			return (-1);
803 	}
804 
805 	return (1);
806 }
807