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