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