xref: /openbsd-src/usr.bin/tftp/main.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1 /*	$OpenBSD: main.c,v 1.46 2023/03/08 04:43:12 guenther 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
main(int argc,char * argv[])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
setpeer(char * host,char * port)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
parsearg(int argc,char * argv[])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
modecmd(int argc,char * argv[])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 void
setbinary(int argc,char * argv[])300 setbinary(int argc, char *argv[])
301 {
302 	settftpmode("octet");
303 }
304 
305 void
setascii(int argc,char * argv[])306 setascii(int argc, char *argv[])
307 {
308 	settftpmode("netascii");
309 }
310 
311 static void
settftpmode(char * newmode)312 settftpmode(char *newmode)
313 {
314 	strlcpy(mode, newmode, sizeof(mode));
315 	if (verbose)
316 		printf("mode set to %s\n", mode);
317 }
318 
319 /*
320  * Send file(s).
321  */
322 void
put(int argc,char * argv[])323 put(int argc, char *argv[])
324 {
325 	int	 fd;
326 	int	 n;
327 	char	*cp, *targ;
328 
329 	if (argc < 2) {
330 		strlcpy(line, "put ", sizeof(line));
331 		printf("(file) ");
332 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
333 		if (makeargv())
334 			return;
335 		argc = margc;
336 		argv = margv;
337 	}
338 	if (argc < 2) {
339 		putusage(argv[0]);
340 		return;
341 	}
342 	targ = argv[argc - 1];
343 	if (strrchr(argv[argc - 1], ':')) {
344 
345 		for (n = 1; n < argc - 1; n++)
346 			if (strchr(argv[n], ':')) {
347 				putusage(argv[0]);
348 				return;
349 			}
350 		cp = argv[argc - 1];
351 		targ = strrchr(cp, ':');
352 		*targ++ = 0;
353 		if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
354 			cp[strlen(cp) - 1] = '\0';
355 			cp++;
356 		}
357 		setpeer(cp, NULL);
358 	}
359 	if (!connected) {
360 		printf("No target machine specified.\n");
361 		return;
362 	}
363 	if (argc < 4) {
364 		cp = argc == 2 ? tail(targ) : argv[1];
365 		fd = open(cp, O_RDONLY);
366 		if (fd < 0) {
367 			warn("open: %s", cp);
368 			return;
369 		}
370 		if (verbose)
371 			printf("putting %s to %s:%s [%s]\n",
372 			    cp, hostname, targ, mode);
373 		sendfile(fd, targ, mode);
374 		return;
375 	}
376 
377 	/*
378 	 * this assumes the target is a directory on
379 	 * on a remote unix system.  hmmmm.
380 	 */
381 	for (n = 1; n < argc - 1; n++) {
382 		if (asprintf(&cp, "%s/%s", targ, tail(argv[n])) == -1)
383 			err(1, "asprintf");
384 		fd = open(argv[n], O_RDONLY);
385 		if (fd < 0) {
386 			warn("open: %s", argv[n]);
387 			free(cp);
388 			continue;
389 		}
390 		if (verbose)
391 			printf("putting %s to %s:%s [%s]\n",
392 			    argv[n], hostname, cp, mode);
393 		sendfile(fd, cp, mode);
394 		free(cp);
395 	}
396 }
397 
398 static void
putusage(char * s)399 putusage(char *s)
400 {
401 	printf("usage: %s file [[host:]remotename]\n", s);
402 	printf("       %s file1 file2 ... fileN [[host:]remote-directory]\n",
403 	    s);
404 }
405 
406 /*
407  * Receive file(s).
408  */
409 void
get(int argc,char * argv[])410 get(int argc, char *argv[])
411 {
412 	int	 fd;
413 	int	 n;
414 	char	*cp;
415 	char	*src;
416 
417 	if (argc < 2) {
418 		strlcpy(line, "get ", sizeof(line));
419 		printf("(files) ");
420 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
421 		if (makeargv())
422 			return;
423 		argc = margc;
424 		argv = margv;
425 	}
426 	if (argc < 2) {
427 		getusage(argv[0]);
428 		return;
429 	}
430 	if (!connected) {
431 		for (n = 1; n < argc; n++)
432 			if (strrchr(argv[n], ':') == 0) {
433 				getusage(argv[0]);
434 				return;
435 			}
436 	}
437 	for (n = 1; n < argc; n++) {
438 		src = strrchr(argv[n], ':');
439 		if (src == NULL)
440 			src = argv[n];
441 		else {
442 			char *cp;
443 
444 			*src++ = 0;
445 			cp = argv[n];
446 			if (cp[0] == '[' && cp[strlen(cp) - 1] == ']') {
447 				cp[strlen(cp) - 1] = '\0';
448 				cp++;
449 			}
450 			setpeer(cp, NULL);
451 			if (!connected)
452 				continue;
453 		}
454 		if (argc < 4) {
455 			cp = argc == 3 ? argv[2] : tail(src);
456 			fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
457 			if (fd < 0) {
458 				warn("create: %s", cp);
459 				return;
460 			}
461 			if (verbose)
462 				printf("getting from %s:%s to %s [%s]\n",
463 				    hostname, src, cp, mode);
464 			recvfile(fd, src, mode);
465 			break;
466 		}
467 		cp = tail(src);	/* new .. jdg */
468 		fd = open(cp, O_CREAT | O_TRUNC | O_WRONLY, 0644);
469 		if (fd < 0) {
470 			warn("create: %s", cp);
471 			continue;
472 		}
473 		if (verbose)
474 			printf("getting from %s:%s to %s [%s]\n",
475 			    hostname, src, cp, mode);
476 		recvfile(fd, src, mode);
477 	}
478 }
479 
480 static void
getusage(char * s)481 getusage(char *s)
482 {
483 	printf("usage: %s [host:]file [localname]\n", s);
484 	printf("       %s [host1:]file1 [host2:]file2 ... [hostN:]fileN\n", s);
485 }
486 
487 void
setrexmt(int argc,char * argv[])488 setrexmt(int argc, char *argv[])
489 {
490 	int		 t;
491 	const char	*errstr;
492 
493 	if (argc < 2) {
494 		strlcpy(line, "Rexmt-timeout ", sizeof(line));
495 		printf("(value) ");
496 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
497 		if (makeargv())
498 			return;
499 		argc = margc;
500 		argv = margv;
501 	}
502 	if (argc != 2) {
503 		printf("usage: %s value\n", argv[0]);
504 		return;
505 	}
506 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
507 	if (errstr)
508 		printf("%s: value is %s\n", argv[1], errstr);
509 	else
510 		rexmtval = t;
511 }
512 
513 void
settimeout(int argc,char * argv[])514 settimeout(int argc, char *argv[])
515 {
516 	int		 t;
517 	const char	*errstr;
518 
519 	if (argc < 2) {
520 		strlcpy(line, "Maximum-timeout ", sizeof(line));
521 		printf("(value) ");
522 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
523 		if (makeargv())
524 			return;
525 		argc = margc;
526 		argv = margv;
527 	}
528 	if (argc != 2) {
529 		printf("usage: %s value\n", argv[0]);
530 		return;
531 	}
532 	t = strtonum(argv[1], TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
533 	if (errstr)
534 		printf("%s: value is %s\n", argv[1], errstr);
535 	else
536 		maxtimeout = t;
537 }
538 
539 void
status(int argc,char * argv[])540 status(int argc, char *argv[])
541 {
542 	if (connected)
543 		printf("Connected to %s.\n", hostname);
544 	else
545 		printf("Not connected.\n");
546 	printf("Mode: %s Verbose: %s Tracing: %s\n",
547 	    mode, verbose ? "on" : "off", trace ? "on" : "off");
548 	printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
549 	    rexmtval, maxtimeout);
550 }
551 
552 void
intr(int signo)553 intr(int signo)
554 {
555 	intrflag = 1;
556 }
557 
558 char *
tail(char * filename)559 tail(char *filename)
560 {
561 	char	*s;
562 
563 	while (*filename) {
564 		s = strrchr(filename, '/');
565 		if (s == NULL)
566 			break;
567 		if (s[1])
568 			return (s + 1);
569 		*s = '\0';
570 	}
571 
572 	return (filename);
573 }
574 
575 /*
576  * Command parser.
577  */
578 static __dead void
command(void)579 command(void)
580 {
581 	struct cmd	*c;
582 
583 	for (;;) {
584 		if (isatty(STDIN_FILENO))
585 			printf("%s> ", prompt);
586 		if (readcmd(line, LBUFLEN, stdin) < 1)
587 			continue;
588 		if ((line[0] == 0) || (line[0] == '\n'))
589 			continue;
590 		if (makeargv())
591 			continue;
592 		if (margc == 0)
593 			continue;
594 		c = getcmd(margv[0]);
595 		if (c == (struct cmd *) - 1) {
596 			printf("?Ambiguous command\n");
597 			continue;
598 		}
599 		if (c == 0) {
600 			printf("?Invalid command\n");
601 			continue;
602 		}
603 		(*c->handler)(margc, margv);
604 	}
605 }
606 
607 struct cmd *
getcmd(char * name)608 getcmd(char *name)
609 {
610 	char		*p, *q;
611 	struct cmd	*c, *found;
612 	int		 nmatches, longest;
613 
614 	longest = 0;
615 	nmatches = 0;
616 	found = 0;
617 	intrflag = 0;
618 	for (c = cmdtab; (p = c->name) != NULL; c++) {
619 		for (q = name; *q == *p++; q++)
620 			if (*q == 0)		/* exact match? */
621 				return (c);
622 		if (!*q) {			/* the name was a prefix */
623 			if (q - name > longest) {
624 				longest = q - name;
625 				nmatches = 1;
626 				found = c;
627 			} else if (q - name == longest)
628 				nmatches++;
629 		}
630 	}
631 	if (nmatches > 1)
632 		return ((struct cmd *) - 1);
633 
634 	return (found);
635 }
636 
637 /*
638  * Slice a string up into argc/argv.
639  */
640 static int
makeargv(void)641 makeargv(void)
642 {
643 	char	 *cp;
644 	char	**argp = margv;
645 	int	  ret = 0;
646 
647 	margc = 0;
648 	for (cp = line; *cp;) {
649 		if (margc >= MAXARGV) {
650 			printf("too many arguments\n");
651 			ret = 1;
652 			break;
653 		}
654 		while (isspace((unsigned char)*cp))
655 			cp++;
656 		if (*cp == '\0')
657 			break;
658 		*argp++ = cp;
659 		margc += 1;
660 		while (*cp != '\0' && !isspace((unsigned char)*cp))
661 			cp++;
662 		if (*cp == '\0')
663 			break;
664 		*cp++ = '\0';
665 	}
666 	*argp++ = 0;
667 
668 	return (ret);
669 }
670 
671 void
quit(int argc,char * argv[])672 quit(int argc, char *argv[])
673 {
674 	exit(0);
675 }
676 
677 /*
678  * Help command.
679  */
680 void
help(int argc,char * argv[])681 help(int argc, char *argv[])
682 {
683 	struct cmd	*c;
684 
685 	if (argc == 1) {
686 		printf("Commands may be abbreviated.  Commands are:\n\n");
687 		for (c = cmdtab; c->name != NULL; c++)
688 			printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
689 		return;
690 	}
691 	while (--argc > 0) {
692 		char *arg;
693 		arg = *++argv;
694 		c = getcmd(arg);
695 		if (c == (struct cmd *) - 1)
696 			printf("?Ambiguous help command %s\n", arg);
697 		else if (c == NULL)
698 			printf("?Invalid help command %s\n", arg);
699 		else
700 			printf("%s\n", c->help);
701 	}
702 }
703 
704 void
settrace(int argc,char * argv[])705 settrace(int argc, char *argv[])
706 {
707 	trace = !trace;
708 	printf("Packet tracing %s.\n", trace ? "on" : "off");
709 }
710 
711 void
setverbose(int argc,char * argv[])712 setverbose(int argc, char *argv[])
713 {
714 	verbose = !verbose;
715 	printf("Verbose mode %s.\n", verbose ? "on" : "off");
716 }
717 
718 void
settsize(int argc,char * argv[])719 settsize(int argc, char *argv[])
720 {
721 	opt_tsize = !opt_tsize;
722 	printf("Tsize option %s.\n", opt_tsize ? "on" : "off");
723 	if (opt_tsize)
724 		has_options++;
725 	else
726 		has_options--;
727 }
728 
729 void
settout(int argc,char * argv[])730 settout(int argc, char *argv[])
731 {
732 	opt_tout = !opt_tout;
733 	printf("Timeout option %s.\n", opt_tout ? "on" : "off");
734 	if (opt_tout)
735 		has_options++;
736 	else
737 		has_options--;
738 }
739 
740 void
setblksize(int argc,char * argv[])741 setblksize(int argc, char *argv[])
742 {
743 	int		 t;
744 	const char	*errstr;
745 
746 	if (argc < 2) {
747 		strlcpy(line, "Blocksize ", sizeof(line));
748 		printf("(value) ");
749 		readcmd(&line[strlen(line)], LBUFLEN - strlen(line), stdin);
750 		if (makeargv())
751 			return;
752 		argc = margc;
753 		argv = margv;
754 	}
755 	if (argc != 2) {
756 		printf("usage: %s value\n", argv[0]);
757 		return;
758 	}
759 	t = strtonum(argv[1], SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
760 	if (errstr)
761 		printf("%s: value is %s\n", argv[1], errstr);
762 	else {
763 		if (opt_blksize == 0)
764 			has_options++;
765 		opt_blksize = t;
766 	}
767 }
768 
769 int
readcmd(char * input,int len,FILE * stream)770 readcmd(char *input, int len, FILE *stream)
771 {
772 	int		nfds;
773 	struct pollfd	pfd[1];
774 
775 	fflush(stdout);
776 
777 	pfd[0].fd = 0;
778 	pfd[0].events = POLLIN;
779 	nfds = poll(pfd, 1, INFTIM);
780 	if (nfds == -1) {
781 		if (intrflag) {
782 			intrflag = 0;
783 			putchar('\n');
784 			return (0);
785 		}
786 		exit(1);
787 	}
788 
789 	if (fgets(input, len, stream) == NULL) {
790 		if (feof(stdin))
791 			exit(0);
792 		else
793 			return (-1);
794 	}
795 
796 	return (1);
797 }
798