xref: /openbsd-src/usr.bin/tftp/main.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: main.c,v 1.34 2013/11/26 21:08:12 deraadt 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/param.h>
40 #include <sys/socket.h>
41 #include <sys/file.h>
42 
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <arpa/tftp.h>
46 
47 #include <ctype.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <netdb.h>
51 #include <poll.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.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[MAXHOSTNAMELEN];
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 char	vhelp[] = "toggle verbose mode";
113 char	thelp[] = "toggle packet tracing";
114 char	chelp[] = "connect to remote tftp";
115 char	qhelp[] = "exit tftp";
116 char	hhelp[] = "print help information";
117 char	shelp[] = "send file";
118 char	rhelp[] = "receive file";
119 char	mhelp[] = "set file transfer mode";
120 char	sthelp[] = "show current status";
121 char	xhelp[] = "set per-packet retransmission timeout";
122 char	ihelp[] = "set total retransmission timeout";
123 char	ashelp[] = "set mode to netascii";
124 char	bnhelp[] = "set mode to octet";
125 char	oshelp[] = "toggle tsize option";
126 char	othelp[] = "toggle timeout option";
127 char	obhelp[] = "set alternative blksize option";
128 
129 struct cmd {
130 	char	*name;
131 	char	*help;
132 	void	 (*handler)(int, char **);
133 };
134 
135 struct cmd cmdtab[] = {
136 	{ "connect",	chelp,	parsearg },
137 	{ "mode",       mhelp,	modecmd },
138 	{ "put",	shelp,	put },
139 	{ "get",	rhelp,	get },
140 	{ "quit",	qhelp,	quit },
141 	{ "verbose",	vhelp,	setverbose },
142 	{ "trace",	thelp,	settrace },
143 	{ "status",	sthelp,	status },
144 	{ "binary",     bnhelp,	setbinary },
145 	{ "ascii",      ashelp,	setascii },
146 	{ "rexmt",	xhelp,	setrexmt },
147 	{ "timeout",	ihelp,	settimeout },
148 	{ "tsize",	oshelp, settsize },
149 	{ "tout",	othelp, settout },
150 	{ "blksize",	obhelp,	setblksize },
151 	{ "help",	hhelp,	help },
152 	{ "?",		hhelp,	help },
153 	{ NULL,		NULL,	NULL }
154 };
155 
156 struct	modes {
157 	char	*m_name;
158 	char	*m_mode;
159 } modes[] = {
160 	{ "ascii",	"netascii" },
161 	{ "netascii",	"netascii" },
162 	{ "binary",	"octet" },
163 	{ "image",	"octet" },
164 	{ "octet",	"octet" },
165 /*	{ "mail",	"mail" }, */
166 	{ NULL,		NULL }
167 };
168 
169 int
170 main(int argc, char *argv[])
171 {
172 	f = -1;
173 
174 	/* set default transfer mode */
175 	strlcpy(mode, "netascii", sizeof(mode));
176 
177 	/* set peer if given */
178 	if (argc > 1)
179 		parsearg(argc, argv);
180 
181 	/* catch SIGINT */
182 	signal(SIGINT, intr);
183 
184 	/* allocate memory for packets */
185 	if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL)
186 		err(1, "malloc");
187 
188 	/* command prompt */
189 	command();
190 
191 	return (0);
192 }
193 
194 void
195 setpeer(char *host, char *port)
196 {
197 	struct addrinfo hints, *res0, *res;
198 	int error;
199 	struct sockaddr_storage ss;
200 	char *cause = "unknown";
201 
202 	if (connected) {
203 		close(f);
204 		f = -1;
205 	}
206 	connected = 0;
207 
208 	memset(&hints, 0, sizeof(hints));
209 	hints.ai_family = PF_UNSPEC;
210 	hints.ai_socktype = SOCK_DGRAM;
211 	hints.ai_protocol = IPPROTO_UDP;
212 	hints.ai_flags = AI_CANONNAME;
213 	if (!port)
214 		port = "tftp";
215 	error = getaddrinfo(host, port, &hints, &res0);
216 	if (error) {
217 		warnx("%s", gai_strerror(error));
218 		return;
219 	}
220 
221 	for (res = res0; res; res = res->ai_next) {
222 		if (res->ai_addrlen > sizeof(peeraddr))
223 			continue;
224 		f = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
225 		if (f < 0) {
226 			cause = "socket";
227 			continue;
228 		}
229 
230 		memset(&ss, 0, sizeof(ss));
231 		ss.ss_family = res->ai_family;
232 		ss.ss_len = res->ai_addrlen;
233 		if (bind(f, (struct sockaddr *)&ss, ss.ss_len) < 0) {
234 			cause = "bind";
235 			close(f);
236 			f = -1;
237 			continue;
238 		}
239 
240 		break;
241 	}
242 
243 	if (f < 0)
244 		warn("%s", cause);
245 	else {
246 		/* res->ai_addr <= sizeof(peeraddr) is guaranteed */
247 		memcpy(&peeraddr, res->ai_addr, res->ai_addrlen);
248 		if (res->ai_canonname) {
249 			(void)strlcpy(hostname, res->ai_canonname,
250 			    sizeof(hostname));
251 		} else
252 			(void)strlcpy(hostname, host, sizeof(hostname));
253 			connected = 1;
254 	}
255 	freeaddrinfo(res0);
256 }
257 
258 void
259 parsearg(int argc, char *argv[])
260 {
261 	if (argc < 2) {
262 		strlcpy(line, "Connect ", sizeof(line));
263 		printf("(to) ");
264 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
265 		if (makeargv())
266 			return;
267 		argc = margc;
268 		argv = margv;
269 	}
270 	if ((argc < 2) || (argc > 3)) {
271 		printf("usage: %s [host [port]]\n", argv[0]);
272 		return;
273 	}
274 	if (argc == 2)
275 		setpeer(argv[1], NULL);
276 	else
277 		setpeer(argv[1], argv[2]);
278 }
279 
280 void
281 modecmd(int argc, char *argv[])
282 {
283 	struct modes	*p;
284 	char		*sep;
285 
286 	if (argc < 2) {
287 		printf("Using %s mode to transfer files.\n", mode);
288 		return;
289 	}
290 	if (argc == 2) {
291 		for (p = modes; p->m_name != NULL; p++)
292 			if (strcmp(argv[1], p->m_name) == 0)
293 				break;
294 		if (p->m_name) {
295 			settftpmode(p->m_mode);
296 			return;
297 		}
298 		printf("%s: unknown mode\n", argv[1]);
299 		/* drop through and print usage message */
300 	}
301 
302 	printf("usage: %s [", argv[0]);
303 	sep = " ";
304 	for (p = modes; p->m_name != NULL; p++) {
305 		printf("%s%s", sep, p->m_name);
306 		if (*sep == ' ')
307 			sep = " | ";
308 	}
309 	printf(" ]\n");
310 
311 	return;
312 }
313 
314 /* ARGSUSED */
315 void
316 setbinary(int argc, char *argv[])
317 {
318 	settftpmode("octet");
319 }
320 
321 /* ARGSUSED */
322 void
323 setascii(int argc, char *argv[])
324 {
325 	settftpmode("netascii");
326 }
327 
328 static void
329 settftpmode(char *newmode)
330 {
331 	strlcpy(mode, newmode, sizeof(mode));
332 	if (verbose)
333 		printf("mode set to %s\n", mode);
334 }
335 
336 /*
337  * Send file(s).
338  */
339 void
340 put(int argc, char *argv[])
341 {
342 	int	 fd;
343 	int	 n;
344 	char	*cp, *targ;
345 
346 	if (argc < 2) {
347 		strlcpy(line, "put ", sizeof(line));
348 		printf("(file) ");
349 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
350 		if (makeargv())
351 			return;
352 		argc = margc;
353 		argv = margv;
354 	}
355 	if (argc < 2) {
356 		putusage(argv[0]);
357 		return;
358 	}
359 	targ = argv[argc - 1];
360 	if (strrchr(argv[argc - 1], ':')) {
361 
362 		for (n = 1; n < argc - 1; n++)
363 			if (strchr(argv[n], ':')) {
364 				putusage(argv[0]);
365 				return;
366 			}
367 		cp = argv[argc - 1];
368 		targ = strrchr(cp, ':');
369 		*targ++ = 0;
370 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
371 			cp[strlen(cp) - 1] = '\0';
372 			cp++;
373 		}
374 		setpeer(cp, NULL);
375 	}
376 	if (!connected) {
377 		printf("No target machine specified.\n");
378 		return;
379 	}
380 	if (argc < 4) {
381 		cp = argc == 2 ? tail(targ) : argv[1];
382 		fd = open(cp, O_RDONLY);
383 		if (fd < 0) {
384 			warn("open: %s", cp);
385 			return;
386 		}
387 		if (verbose)
388 			printf("putting %s to %s:%s [%s]\n",
389 			    cp, hostname, targ, mode);
390 		sendfile(fd, targ, mode);
391 		return;
392 	}
393 
394 	/*
395 	 * this assumes the target is a directory on
396 	 * on a remote unix system.  hmmmm.
397 	 */
398 	for (n = 1; n < argc - 1; n++) {
399 		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
400 			err(1, "asprintf");
401 		fd = open(argv[n], O_RDONLY);
402 		if (fd < 0) {
403 			warn("open: %s", argv[n]);
404 			free(cp);
405 			continue;
406 		}
407 		if (verbose)
408 			printf("putting %s to %s:%s [%s]\n",
409 			    argv[n], hostname, cp, mode);
410 		sendfile(fd, cp, mode);
411 		free(cp);
412 	}
413 }
414 
415 static void
416 putusage(char *s)
417 {
418 	printf("usage: %s file [[host:]remotename]\n", s);
419 	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
420 	    s);
421 }
422 
423 /*
424  * Receive file(s).
425  */
426 void
427 get(int argc, char *argv[])
428 {
429 	int	 fd;
430 	int	 n;
431 	char	*cp;
432 	char	*src;
433 
434 	if (argc < 2) {
435 		strlcpy(line, "get ", sizeof(line));
436 		printf("(files) ");
437 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
438 		if (makeargv())
439 			return;
440 		argc = margc;
441 		argv = margv;
442 	}
443 	if (argc < 2) {
444 		getusage(argv[0]);
445 		return;
446 	}
447 	if (!connected) {
448 		for (n = 1; n < argc; n++)
449 			if (strrchr(argv[n], ':') == 0) {
450 				getusage(argv[0]);
451 				return;
452 			}
453 	}
454 	for (n = 1; n < argc; n++) {
455 		src = strrchr(argv[n], ':');
456 		if (src == NULL)
457 			src = argv[n];
458 		else {
459 			char *cp;
460 
461 			*src++ = 0;
462 			cp = argv[n];
463 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
464 				cp[strlen(cp) - 1] = '\0';
465 				cp++;
466 			}
467 			setpeer(cp, NULL);
468 			if (!connected)
469 				continue;
470 		}
471 		if (argc < 4) {
472 			cp = argc == 3 ? argv[2] : tail(src);
473 			fd = creat(cp, 0644);
474 			if (fd < 0) {
475 				warn("create: %s", cp);
476 				return;
477 			}
478 			if (verbose)
479 				printf("getting from %s:%s to %s [%s]\n",
480 				    hostname, src, cp, mode);
481 			recvfile(fd, src, mode);
482 			break;
483 		}
484 		cp = tail(src);	/* new .. jdg */
485 		fd = creat(cp, 0644);
486 		if (fd < 0) {
487 			warn("create: %s", cp);
488 			continue;
489 		}
490 		if (verbose)
491 			printf("getting from %s:%s to %s [%s]\n",
492 			    hostname, src, cp, mode);
493 		recvfile(fd, src, mode);
494 	}
495 }
496 
497 static void
498 getusage(char *s)
499 {
500 	printf("usage: %s [host:]file [localname]\n", s);
501 	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
502 }
503 
504 void
505 setrexmt(int argc, char *argv[])
506 {
507 	int		 t;
508 	const char	*errstr;
509 
510 	if (argc < 2) {
511 		strlcpy(line, "Rexmt-timeout ", sizeof(line));
512 		printf("(value) ");
513 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
514 		if (makeargv())
515 			return;
516 		argc = margc;
517 		argv = margv;
518 	}
519 	if (argc != 2) {
520 		printf("usage: %s value\n", argv[0]);
521 		return;
522 	}
523 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
524 	if (errstr)
525 		printf("%s: value is %s\n", argv[1], errstr);
526 	else
527 		rexmtval = t;
528 }
529 
530 void
531 settimeout(int argc, char *argv[])
532 {
533 	int		 t;
534 	const char	*errstr;
535 
536 	if (argc < 2) {
537 		strlcpy(line, "Maximum-timeout ", sizeof(line));
538 		printf("(value) ");
539 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
540 		if (makeargv())
541 			return;
542 		argc = margc;
543 		argv = margv;
544 	}
545 	if (argc != 2) {
546 		printf("usage: %s value\n", argv[0]);
547 		return;
548 	}
549 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
550 	if (errstr)
551 		printf("%s: value is %s\n", argv[1], errstr);
552 	else
553 		maxtimeout = t;
554 }
555 
556 /* ARGSUSED */
557 void
558 status(int argc, char *argv[])
559 {
560 	if (connected)
561 		printf("Connected to %s.\n", hostname);
562 	else
563 		printf("Not connected.\n");
564 	printf("Mode: %s Verbose: %s Tracing: %s\n",
565 	    mode, verbose ? "on" : "off", trace ? "on" : "off");
566 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
567 	    rexmtval, maxtimeout);
568 }
569 
570 /* ARGSUSED */
571 void
572 intr(int signo)
573 {
574 	intrflag = 1;
575 }
576 
577 char *
578 tail(char *filename)
579 {
580 	char	*s;
581 
582 	while (*filename) {
583 		s = strrchr(filename, '/');
584 		if (s == NULL)
585 			break;
586 		if (s[1])
587 			return (s + 1);
588 		*s = '\0';
589 	}
590 
591 	return (filename);
592 }
593 
594 /*
595  * Command parser.
596  */
597 static __dead void
598 command(void)
599 {
600 	struct cmd	*c;
601 
602 	for (;;) {
603 		printf("%s> ", prompt);
604 		if (readcmd(line, LBUFLEN, stdin) < 1)
605 			continue;
606 		if ((line[0] == 0) || (line[0] == '\n'))
607 			continue;
608 		if (makeargv())
609 			continue;
610 		if (margc == 0)
611 			continue;
612 		c = getcmd(margv[0]);
613 		if (c == (struct cmd *) - 1) {
614 			printf("?Ambiguous command\n");
615 			continue;
616 		}
617 		if (c == 0) {
618 			printf("?Invalid command\n");
619 			continue;
620 		}
621 		(*c->handler)(margc, margv);
622 	}
623 }
624 
625 struct cmd *
626 getcmd(char *name)
627 {
628 	char		*p, *q;
629 	struct cmd	*c, *found;
630 	int		 nmatches, longest;
631 
632 	longest = 0;
633 	nmatches = 0;
634 	found = 0;
635 	intrflag = 0;
636 	for (c = cmdtab; (p = c->name) != NULL; c++) {
637 		for (q = name; *q == *p++; q++)
638 			if (*q == 0)		/* exact match? */
639 				return (c);
640 		if (!*q) {			/* the name was a prefix */
641 			if (q - name > longest) {
642 				longest = q - name;
643 				nmatches = 1;
644 				found = c;
645 			} else if (q - name == longest)
646 				nmatches++;
647 		}
648 	}
649 	if (nmatches > 1)
650 		return ((struct cmd *) - 1);
651 
652 	return (found);
653 }
654 
655 /*
656  * Slice a string up into argc/argv.
657  */
658 static int
659 makeargv(void)
660 {
661 	char	 *cp;
662 	char	**argp = margv;
663 	int	  ret = 0;
664 
665 	margc = 0;
666 	for (cp = line; *cp;) {
667 		if (margc >= MAXARGV) {
668 			printf("too many arguments\n");
669 			ret = 1;
670 			break;
671 		}
672 		while (isspace((unsigned char)*cp))
673 			cp++;
674 		if (*cp == '\0')
675 			break;
676 		*argp++ = cp;
677 		margc += 1;
678 		while (*cp != '\0' && !isspace((unsigned char)*cp))
679 			cp++;
680 		if (*cp == '\0')
681 			break;
682 		*cp++ = '\0';
683 	}
684 	*argp++ = 0;
685 
686 	return (ret);
687 }
688 
689 /* ARGSUSED */
690 void
691 quit(int argc, char *argv[])
692 {
693 	exit(0);
694 }
695 
696 /*
697  * Help command.
698  */
699 void
700 help(int argc, char *argv[])
701 {
702 	struct cmd	*c;
703 
704 	if (argc == 1) {
705 		printf("Commands may be abbreviated.  Commands are:\n\n");
706 		for (c = cmdtab; c->name != NULL; c++)
707 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
708 		return;
709 	}
710 	while (--argc > 0) {
711 		char *arg;
712 		arg = *++argv;
713 		c = getcmd(arg);
714 		if (c == (struct cmd *) - 1)
715 			printf("?Ambiguous help command %s\n", arg);
716 		else if (c == (struct cmd *)0)
717 			printf("?Invalid help command %s\n", arg);
718 		else
719 			printf("%s\n", c->help);
720 	}
721 }
722 
723 /* ARGSUSED */
724 void
725 settrace(int argc, char *argv[])
726 {
727 	trace = !trace;
728 	printf("Packet tracing %s.\n", trace ? "on" : "off");
729 }
730 
731 /* ARGSUSED */
732 void
733 setverbose(int argc, char *argv[])
734 {
735 	verbose = !verbose;
736 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
737 }
738 
739 /* ARGSUSED */
740 void
741 settsize(int argc, char *argv[])
742 {
743 	opt_tsize = !opt_tsize;
744 	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
745 	if (opt_tsize)
746 		has_options++;
747 	else
748 		has_options--;
749 }
750 
751 /* ARGSUSED */
752 void
753 settout(int argc, char *argv[])
754 {
755 	opt_tout = !opt_tout;
756 	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
757 	if (opt_tout)
758 		has_options++;
759 	else
760 		has_options--;
761 }
762 
763 void
764 setblksize(int argc, char *argv[])
765 {
766 	int		 t;
767 	const char	*errstr;
768 
769 	if (argc < 2) {
770 		strlcpy(line, "Blocksize ", sizeof(line));
771 		printf("(value) ");
772 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
773 		if (makeargv())
774 			return;
775 		argc = margc;
776 		argv = margv;
777 	}
778 	if (argc != 2) {
779 		printf("usage: %s value\n", argv[0]);
780 		return;
781 	}
782 	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
783 	if (errstr)
784 		printf("%s: value is %s\n", argv[1], errstr);
785 	else {
786 		if (opt_blksize == 0)
787 			has_options++;
788 		opt_blksize = t;
789 	}
790 }
791 
792 int
793 readcmd(char *input, int len, FILE *stream)
794 {
795 	int		nfds;
796 	struct pollfd	pfd[1];
797 
798 	fflush(stdout);
799 
800 	pfd[0].fd = 0;
801 	pfd[0].events = POLLIN;
802 	nfds = poll(pfd, 1, INFTIM);
803 	if (nfds == -1) {
804 		if (intrflag) {
805 			intrflag = 0;
806 			putchar('\n');
807 			return (0);
808 		}
809 		exit(1);
810 	}
811 
812 	if (fgets(input, len, stream) == NULL) {
813 		if (feof(stdin))
814 			exit(0);
815 		else
816 			return (-1);
817 	}
818 
819 	return (1);
820 }
821